// Generated by CoffeeScript 1.12.4 /* jslint debug: true */ /* DEPRECATED These extensions to the JavaScript Array.prototype are no longer used internally and their use by clients is deprecated. */ (function() { var arrayRemoveObject, arrayShuffle, solutionsContainsAllele, hasProp = {}.hasOwnProperty, indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; Array.prototype.remove = function(from, to) { var rest; rest = this.slice((to || from) + 1 || this.length); this.length = from < 0 ? this.length + from : from; return this.push.apply(this, rest); }; Array.prototype.removeObj = function(obj) { var i; i = this.indexOf(obj); if (~i) { this.remove(i); return true; } else { return false; } }; Array.prototype.replaceFirst = function(obj, replacement) { return this[this.indexOf(obj)] = replacement; }; Array.prototype.shuffle = function() { var current, tmp, top; top = this.length; if (top) { while (--top) { current = Math.floor(Math.random() * (top + 1)); tmp = this[current]; this[current] = this[top]; this[top] = tmp; } } return this; }; /* jslint debug: true */ arrayRemoveObject = function(array, obj) { var i; i = array.indexOf(obj); if (i >= 0) { array.splice(i, 1); return true; } else { return false; } }; arrayShuffle = function(array) { var current, tmp, top; top = array.length; if (top) { while (--top) { current = Math.floor(Math.random() * (top + 1)); tmp = array[current]; array[current] = array[top]; array[top] = tmp; } } return array; }; window.ExtMath = {}; ExtMath.randomInt = function(max) { return Math.floor(Math.random() * max); }; ExtMath.flip = function() { return ExtMath.randomInt(2); }; window.BioLogica = {}; BioLogica.FEMALE = 1; BioLogica.MALE = 0; BioLogica.combinations = function(arr) { var combo, combos, currentOpts, l, len, len1, m, opts, r, result; result = []; currentOpts = arr[0]; if (arr.length === 1) { return currentOpts.slice(0); } combos = BioLogica.combinations(arr.slice(1)); for (l = 0, len = combos.length; l < len; l++) { combo = combos[l]; if (typeof combo === "string") { combo = [combo]; } for (m = 0, len1 = currentOpts.length; m < len1; m++) { opts = currentOpts[m]; r = combo.slice(0); r.unshift(opts); result.push(r); } } return result.slice(0); }; BioLogica.Chromosome = (function() { function Chromosome(species1, chromosome3, side, alleles) { var al, i, l, ref, s, seenGenes; this.species = species1; this.chromosome = chromosome3; seenGenes = []; this.alleles = alleles.sort((function(_this) { return function(a, b) { if (_this.getAllelesPosition(a) > _this.getAllelesPosition(b)) { return 1; } else { return -1; } }; })(this)).filter((function(_this) { return function(item) { var gene; gene = _this.getGeneOfAllele(item); if (seenGenes.indexOf(gene) !== -1) { if (typeof console !== "undefined" && console !== null) { console.warn("Duplicate allele found: " + item); } return false; } seenGenes.push(gene); return true; }; })(this)); if (typeof side === "object") { this.side = side[0]; } else { this.side = side; } this.allelesWithSides = []; for (i = l = 0, ref = this.alleles.length; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { al = this.alleles[i]; s = typeof side === "object" ? side[i] : this.side; this.allelesWithSides.push({ allele: al, side: s }); } } Chromosome.prototype.clone = function(newSide) { return new BioLogica.Chromosome(this.species, this.chromosome, newSide || this.side, this.alleles.slice(0)); }; Chromosome.prototype.lengthInCentimorgans = 100; Chromosome.prototype.getlengthInBasePairs = function() { return this.species.chromosomesLength[this.chromosome]; }; Chromosome.prototype.getGeneOfAllele = function(allele) { var ref; return (ref = BioLogica.Genetics.getGeneOfAllele(this.species, allele)) != null ? ref.name : void 0; }; Chromosome.prototype.getAllelesPosition = function(allele) { var geneName, ref; geneName = this.getGeneOfAllele(allele); return ((ref = this.species.geneList[geneName]) != null ? ref.start : void 0) || -1; }; Chromosome.prototype.replaceAllele = function(prevAllele, newAllele) { var index; index = this.alleles.indexOf(prevAllele); if (index >= 0) { return this.alleles[index] = newAllele; } }; return Chromosome; })(); BioLogica.Chromosome.createChromosome = function(chr1, chr2, crossPoint) { var allele, chromo, crossedAlleles, i, l, len, newAlleles, newSides, ref; newAlleles = []; newSides = []; crossedAlleles = []; ref = chr1.alleles; for (i = l = 0, len = ref.length; l < len; i = ++l) { allele = ref[i]; if (chr1.getAllelesPosition(allele) < crossPoint) { newAlleles.push(allele); newSides.push(chr1.allelesWithSides[i].side); } else { newAlleles.push(chr2.alleles[i]); newSides.push(chr2.allelesWithSides[i].side); crossedAlleles.push([allele, chr2.alleles[i]]); } } chromo = new BioLogica.Chromosome(chr1.species, chr1.chromosome, newSides, newAlleles); chromo.crossedAlleles = crossedAlleles; return chromo; }; /* The constructor should be passed a genotypeHash, of the form below, and this will be copied to the @chromosomes property. genotypeHash = { 1: { a: ["Tk", "M", "w"] b: ["t", "m", "W"] } 2: { a:..., b:... } XY: { x1:..., x2:... } } */ BioLogica.Genotype = (function() { function Genotype(species, genotypeHash, sex1) { var alleles, chromosome, ref, side, sides; this.sex = sex1; this.chromosomes = {}; this.allAlleles = []; for (chromosome in genotypeHash) { if (!hasProp.call(genotypeHash, chromosome)) continue; sides = genotypeHash[chromosome]; this.chromosomes[chromosome] = {}; for (side in sides) { if (!hasProp.call(sides, side)) continue; alleles = sides[side]; if (side === "y") { alleles = []; } this.chromosomes[chromosome][side] = new BioLogica.Chromosome(species, chromosome, side, alleles.slice(0)); this.allAlleles = this.allAlleles.concat(alleles.slice(0)); } } if (this.sex == null) { this.sex = ((ref = this.chromosomes.XY) != null ? ref.y : void 0) != null ? BioLogica.MALE : BioLogica.FEMALE; } } Genotype.prototype.containsAlleles = function(alleles) { var allAllelesCopy, allele, l, len; allAllelesCopy = this.allAlleles.slice(0); if (this.sex === BioLogica.MALE) { allAllelesCopy.push("Y"); } for (l = 0, len = alleles.length; l < len; l++) { allele = alleles[l]; if (!arrayRemoveObject(allAllelesCopy, allele)) { return false; } } return true; }; Genotype.prototype.replaceAlleleChromName = function(chromName, side, allele, newAllele) { var chromosome, chromosomePair; chromosomePair = this.chromosomes[chromName]; chromosome = (chromosomePair != null) && chromosomePair[side]; if (chromosome != null) { return this.replaceAllele(chromosome, allele, newAllele); } }; Genotype.prototype.replaceAllele = function(chromosome, allele, newAllele) { var index; chromosome.replaceAllele(allele, newAllele); index = this.allAlleles.indexOf(allele); if (index >= 0) { return this.allAlleles[index] = newAllele; } }; Genotype.prototype.getAlleleString = function(genes, genetics) { var allele, alleleString, alleles, bAllele, c, chromosome, chromosomes, i, l, len, otherSide, ref, ref1, ref2, ref3, side; if (genes == null) { genes = []; } if (genetics == null) { genetics = null; } alleleString = ""; ref = this.chromosomes; for (c in ref) { if (!hasProp.call(ref, c)) continue; chromosomes = ref[c]; for (side in chromosomes) { if (!hasProp.call(chromosomes, side)) continue; chromosome = chromosomes[side]; alleles = chromosome.alleles; otherSide = side === "x" ? "y" : side === "x1" ? "x2" : "b"; if (side === "x" || side === "x1") { side = "a"; } if (side !== "a") { continue; } for (i = l = 0, len = alleles.length; l < len; i = ++l) { allele = alleles[i]; if ((genes.length === 0) || (ref1 = genetics.geneForAllele(allele), indexOf.call(genes, ref1) >= 0)) { alleleString += side + ":" + allele + ","; if (chromosomes[otherSide]) { bAllele = (ref2 = chromosomes[otherSide]) != null ? ref2.alleles[i] : void 0; if (bAllele) { alleleString += "b:" + ((ref3 = chromosomes[otherSide]) != null ? ref3.alleles[i] : void 0) + ","; } } } } } } return alleleString.substring(0, alleleString.length - 1); }; return Genotype; })(); BioLogica.Genetics = (function() { function Genetics(species1, alleles1, sex) { var genotypeHash; this.species = species1; this.alleles = alleles1; genotypeHash = typeof this.alleles === "string" ? this.convertAlleleStringToGenotypeHash(this.alleles, sex) : this.alleles; this.topUpChromosomes(genotypeHash); this.genotype = new BioLogica.Genotype(this.species, genotypeHash, sex); } /* Converts an alleleString to a genotype hash e.g. convertAlleleStringToChromosomes("a:t,b:t,a:h,b:H,a:Dl", female) => {"1": {a: ["t"], b: ["t"]}, "2": {a: ["h"], b: ["H"]}, "XY": {x1: ["Dl"]}} Also supports limited options for alleles, e.g. "a:T/Tk" or b:x/y/z */ Genetics.prototype.convertAlleleStringToGenotypeHash = function(alleleString, sex) { var allele, alleles, chromoName, genotypeHash, l, len, len1, m, ref, side, sides, split; split = BioLogica.Genetics.parseAlleleString(alleleString); genotypeHash = {}; ref = this.species.chromosomeNames; for (l = 0, len = ref.length; l < len; l++) { chromoName = ref[l]; genotypeHash[chromoName] = {}; sides = this.getSides(chromoName, sex); genotypeHash[chromoName][sides[0]] = []; genotypeHash[chromoName][sides[1]] = []; } for (side in split) { if (!hasProp.call(split, side)) continue; alleles = split[side]; if (!alleles) { continue; } for (m = 0, len1 = alleles.length; m < len1; m++) { allele = alleles[m]; if (~allele.indexOf("/")) { allele = this.selectOption(allele); } chromoName = this.findChromosome(allele); if (!chromoName) { continue; } sides = this.getSides(chromoName, sex); genotypeHash[chromoName][side === "a" ? sides[0] : sides[1]].push(allele); } } return genotypeHash; }; Genetics.prototype.selectOption = function(alleles) { var allele, alleleOptions, rand; alleleOptions = (function() { var l, len, ref, results; ref = alleles.split("/"); results = []; for (l = 0, len = ref.length; l < len; l++) { allele = ref[l]; results.push(allele.trim()); } return results; })(); rand = Math.floor(Math.random() * alleleOptions.length); return alleleOptions[rand]; }; Genetics.prototype.getAlleleString = function() { return this.genotype.getAlleleString(); }; Genetics.prototype.getAlleleStringForTrait = function(trait) { var a, allAlleles, alleles, characteristic, gene, genes, l, len, ref; genes = []; allAlleles = []; ref = this.species.traitRules[trait]; for (characteristic in ref) { if (!hasProp.call(ref, characteristic)) continue; alleles = ref[characteristic]; allAlleles = allAlleles.concat(alleles.reduce(function(a, b) { return a.concat(b); }, [])); } allAlleles = allAlleles.reduce(function(p, c) { if (p.indexOf(c) < 0) { p.push(c); } return p; }, []); for (l = 0, len = allAlleles.length; l < len; l++) { a = allAlleles[l]; gene = this.geneForAllele(a); if (indexOf.call(genes, gene) < 0) { genes.push(this.geneForAllele(a)); } } return this.genotype.getAlleleString(genes, this); }; /* "tops-up" the chromosomes: fills in any missing genes with random alleles. At the moment this assumes that all chromosomes have been specified, even if they don't all have all the possible genes */ Genetics.prototype.topUpChromosomes = function(genotypeHash) { var chromoName, chromosome, gene, genes, results, side; results = []; for (chromoName in genotypeHash) { if (!hasProp.call(genotypeHash, chromoName)) continue; chromosome = genotypeHash[chromoName]; genes = this.species.chromosomeGeneMap[chromoName]; results.push((function() { var l, len, results1; results1 = []; for (l = 0, len = genes.length; l < len; l++) { gene = genes[l]; results1.push((function() { var results2; results2 = []; for (side in chromosome) { if (!hasProp.call(chromosome, side)) continue; if (!(this.chromosomeContainsGene(genotypeHash[chromoName][side], gene) || side === "y")) { results2.push(genotypeHash[chromoName][side].push(this.getRandomAllele(gene))); } else { results2.push(void 0); } } return results2; }).call(this)); } return results1; }).call(this)); } return results; }; Genetics.prototype.getSides = function(chromoName, sex) { if (chromoName !== "XY") { return ["a", "b"]; } else { if (sex === BioLogica.FEMALE) { return ["x1", "x2"]; } else { return ["x", "y"]; } } }; /* Returns true if the allele passed is a member of the gene, where the gene is indicated by an example allele. isAlleleOfGene("dl", "D") => true isAlleleOfGene("rh", "D") => false */ Genetics.prototype.isAlleleOfGene = function(allele, exampleOfGene) { var allelesOfGene, gene, ref; ref = this.species.geneList; for (gene in ref) { if (!hasProp.call(ref, gene)) continue; allelesOfGene = this.species.geneList[gene].alleles; if (indexOf.call(allelesOfGene, allele) >= 0 && indexOf.call(allelesOfGene, exampleOfGene) >= 0) { return true; } } return false; }; Genetics.prototype.geneForAllele = function(allele) { var allelesOfGene, gene, ref; ref = this.species.geneList; for (gene in ref) { if (!hasProp.call(ref, gene)) continue; allelesOfGene = this.species.geneList[gene].alleles; if (indexOf.call(allelesOfGene, allele) >= 0) { return gene; } } }; /* Finds the chromosome that a given allele is part of */ Genetics.prototype.findChromosome = function(allele) { var chromosome, gene, genes, l, len, ref; ref = this.species.chromosomeGeneMap; for (chromosome in ref) { genes = ref[chromosome]; for (l = 0, len = genes.length; l < len; l++) { gene = genes[l]; if (this.isAlleleOfGene(allele, gene)) { return chromosome; } } } return false; }; /* Returns true if chromosome array contains any allele of the gene */ Genetics.prototype.chromosomeContainsGene = function(chromosome, exampleOfGene) { var allele, l, len; for (l = 0, len = chromosome.length; l < len; l++) { allele = chromosome[l]; if (this.isAlleleOfGene(allele, exampleOfGene)) { return true; } } return false; }; /* Returns random allele of the gene */ Genetics.prototype.getRandomAllele = function(exampleOfGene) { var _allelesOfGene, allelesOfGene, gene, rand, ref; ref = this.species.geneList; for (gene in ref) { if (!hasProp.call(ref, gene)) continue; _allelesOfGene = this.species.geneList[gene].alleles; if (indexOf.call(_allelesOfGene, exampleOfGene) >= 0) { allelesOfGene = _allelesOfGene; break; } } rand = Math.floor(Math.random() * allelesOfGene.length); return allelesOfGene[rand]; }; /* Given an array of alleles and an array of genes, filter the alleles to return only those alleles that are included in the array of genes. filter(["Tk", "m", "W", "dl"], ["T", "D"]) => ["Tk", "dl"] */ Genetics.prototype.filter = function(alleles, filter) { return alleles.filter((function(_this) { return function(allele) { var gene, l, len; for (l = 0, len = filter.length; l < len; l++) { gene = filter[l]; if (_this.isAlleleOfGene(allele, gene)) { return true; } } return false; }; })(this)); }; /* Returns four haploid cells, with optional crossover */ Genetics.prototype.performMeiosis = function(performCrossover) { var cell, cells, chroma, chromaId, chromoName, chromosome, chromosomes, containsYchromosome, cr, cross, crossInfo, endCellInfo, i, j, l, len, m, ref, ref1, side, sisterChromatidIds, sisterChromatids; cells = [{}, {}, {}, {}]; crossInfo = {}; endCellInfo = {}; ref = this.genotype.chromosomes; for (chromoName in ref) { if (!hasProp.call(ref, chromoName)) continue; chromosomes = ref[chromoName]; sisterChromatids = {}; sisterChromatidIds = ["b2", "b1", "a2", "a1"]; containsYchromosome = false; for (side in chromosomes) { if (!hasProp.call(chromosomes, side)) continue; chromosome = chromosomes[side]; sisterChromatids[sisterChromatidIds.pop()] = chromosome.clone(); sisterChromatids[sisterChromatidIds.pop()] = chromosome.clone(); if (side === "y") { containsYchromosome = true; } } cross = null; if (performCrossover && !containsYchromosome) { cross = this.crossover(sisterChromatids); } sisterChromatidIds = arrayShuffle(["b2", "b1", "a2", "a1"]); endCellInfo[chromoName] = {}; for (i = l = 0, len = cells.length; l < len; i = ++l) { cell = cells[i]; chromaId = sisterChromatidIds[i]; chroma = sisterChromatids[chromaId]; if (!performCrossover) { chroma.side = this.getHaploidChromatidSide(chroma); } cell[chromoName] = chroma; endCellInfo[chromoName][chromaId] = i; if (cross != null) { for (j = m = 0, ref1 = cross.length; 0 <= ref1 ? m < ref1 : m > ref1; j = 0 <= ref1 ? ++m : --m) { cr = cross[j]; if (cr.start === chromaId) { cr.start_cell = i; } if (cr.end === chromaId) { cr.end_cell = i; } } } } crossInfo[chromoName] = cross; } return { cells: cells, crossInfo: crossInfo, endCellInfo: endCellInfo }; }; Genetics.prototype.getHaploidChromatidSide = function(chromatid) { if (chromatid.side === "b") { return "a"; } else if (chromatid.side === "x1" || chromatid.side === "x2") { return "x"; } else { return chromatid.side; } }; Genetics.prototype.crossover = function(sisterChromatids) { var crossoverPoints, crossovers, endSide, l, len, newChromatids, point, startSide; crossoverPoints = this.createCrossoverPoints(sisterChromatids.a1); crossovers = []; for (l = 0, len = crossoverPoints.length; l < len; l++) { point = crossoverPoints[l]; startSide = ["a1", "a2"][ExtMath.flip()]; endSide = ["b1", "b2"][ExtMath.flip()]; newChromatids = this.crossChromatids(sisterChromatids[startSide], sisterChromatids[endSide], point); sisterChromatids[startSide] = newChromatids[0]; sisterChromatids[endSide] = newChromatids[1]; crossovers.push({ start: startSide, end: endSide, point: point, crossedAlleles: newChromatids[0].crossedAlleles.slice(0) }); } return crossovers; }; Genetics.prototype.crossChromatids = function(chr1, chr2, point) { return [BioLogica.Chromosome.createChromosome(chr1, chr2, point), BioLogica.Chromosome.createChromosome(chr2, chr1, point)]; }; /* Create an array of locations (in base pairs) where the parent pair of chromosomes will cross First, randomly select the 10 cM segments that will experience crossover events. Give every 10 cM segment of the chromosome an independent probability of 0.2 of experiencing a crossover. This will result in having between 0 and num_10cM_segments crossover events. If the number of crossover events is greater than three then randomly drop all but three of the crossover events. For each remaining event, determine the exact location of the crossover using a uniform random distribution from the start to the end of the 10 cM segment. */ Genetics.prototype.createCrossoverPoints = function(chromatid) { var crossoverPoint, crossoverPoints, i, l, len, lengthOfDM, positionOnDM, totalDeciMorgans; totalDeciMorgans = Math.floor(chromatid.lengthInCentimorgans / 10); crossoverPoints = (function() { var l, ref, results; results = []; for (i = l = 0, ref = totalDeciMorgans; 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { if (Math.random() < 0.2) { results.push(i); } } return results; })(); while (crossoverPoints.length > 3) { crossoverPoints.splice(ExtMath.randomInt(crossoverPoints.length), 1); } lengthOfDM = chromatid.getlengthInBasePairs() / totalDeciMorgans; for (i = l = 0, len = crossoverPoints.length; l < len; i = ++l) { crossoverPoint = crossoverPoints[i]; positionOnDM = ExtMath.randomInt(lengthOfDM); crossoverPoints[i] = (crossoverPoint * lengthOfDM) + positionOnDM; } return crossoverPoints; }; return Genetics; })(); /* Class methods (non-instance) */ /* Parses the original Java BioLogica allele format and returns an object with a and b representing the alleles on each side. parseAlleleString("a:h,b:H,a:t,b:t,a:Dl,b:D") => { a: ["h", "t", "Dl"], b: ["H", "t", "D"] } */ BioLogica.Genetics.parseAlleleString = function(alleleString) { var ref, ref1; return { a: (ref = alleleString.match(/a:([^,])*/g)) != null ? ref.map(function(str) { var ref1; return (ref1 = str.match(/[^:]+$/)) != null ? ref1[0] : void 0; }) : void 0, b: (ref1 = alleleString.match(/b:([^,])*/g)) != null ? ref1.map(function(str) { var ref2; return (ref2 = str.match(/[^:]+$/)) != null ? ref2[0] : void 0; }) : void 0 }; }; BioLogica.Genetics.getGeneOfAllele = function(species, allele) { var gene, geneName, ref; ref = species.geneList; for (geneName in ref) { if (!hasProp.call(ref, geneName)) continue; gene = ref[geneName]; if (~gene.alleles.indexOf(allele)) { gene.name = geneName; return gene; } } }; /* Goes through the traitRules to find out what unique alleles are associated with each trait e.g. For "tail" it will return ["T", "Tk", "t"]. Adapted from: @see https://github.com/concord-consortium/Geniverse-SproutCore/blob/master/frameworks/geniverse/controllers/match.js */ BioLogica.Genetics.collectAllAllelesForTrait = function(trait, traitRules) { var allele, alleles, allelesHash, characteristic, i, ii, possibileAllelesCombo; allelesHash = {}; alleles = []; for (characteristic in traitRules[trait]) { for (possibileAllelesCombo in traitRules[trait][characteristic]) { if (traitRules[trait][characteristic].hasOwnProperty(possibileAllelesCombo)) { i = 0; ii = traitRules[trait][characteristic][possibileAllelesCombo].length; while (i < ii) { allelesHash[traitRules[trait][characteristic][possibileAllelesCombo][i]] = 1; i++; } } } } for (allele in allelesHash) { alleles.push(allele); } return alleles; }; /* Goes through the traitRules to find out what unique genes are associated with the characteristic. For many characteristics, this will return the same as the above function, but for some with epistasis (one gene trumping others), it will return only those alleles that are relavant. e.g. For "Steel" it will return ["Color", "Metalic", "Brown", "Dilute"]. For "Albino" it will return ["Color"] */ BioLogica.Genetics.collectAllGenesForCharacteristic = function(trait, characteristic, species) { var allele, allelesHash, genes, i, ii, possibileAllelesCombo, traitRules; traitRules = species.traitRules; allelesHash = {}; genes = []; for (possibileAllelesCombo in traitRules[trait][characteristic]) { if (traitRules[trait][characteristic].hasOwnProperty(possibileAllelesCombo)) { i = 0; ii = traitRules[trait][characteristic][possibileAllelesCombo].length; while (i < ii) { allelesHash[traitRules[trait][characteristic][possibileAllelesCombo][i]] = 1; i++; } } } for (allele in allelesHash) { genes.push(BioLogica.Genetics.getGeneOfAllele(species, allele)); } genes = genes.reduce(function(p, c) { if (p.indexOf(c) < 0) { p.push(c); } return p; }, []); return genes; }; BioLogica.Phenotype = (function() { function Phenotype(genetics) { var alleles, l, len, possibleAlleles, possibleCharacteristic, possibleCharacteristics, ref, trait; this.characteristics = {}; this.allCharacteristics = []; ref = genetics.species.traitRules; for (trait in ref) { if (!hasProp.call(ref, trait)) continue; possibleCharacteristics = ref[trait]; for (possibleCharacteristic in possibleCharacteristics) { if (!hasProp.call(possibleCharacteristics, possibleCharacteristic)) continue; possibleAlleles = possibleCharacteristics[possibleCharacteristic]; for (l = 0, len = possibleAlleles.length; l < len; l++) { alleles = possibleAlleles[l]; if (genetics.genotype.containsAlleles(alleles)) { this.characteristics[trait] = possibleCharacteristic; this.allCharacteristics.push(possibleCharacteristic); break; } } if (this.characteristics[trait]) { break; } } } } return Phenotype; })(); /* Returns true if the specified set of solutions includes the specified allele (or "Y"). */ solutionsContainsAllele = function(solutions, allele) { var l, len, solution; for (l = 0, len = solutions.length; l < len; l++) { solution = solutions[l]; if (solution.indexOf(allele) >= 0) { return true; } } return false; }; /* Returns the number of separate changes, including allele changes and sex changes, required to match the phenotype of the 'testOrganism' to that of the 'targetOrganism'. xAlleles is an allele string that represents the alleles to be added in the case of a male test organism being compared to a female target. If not specified, they will be randomly generated, which can lead to inconsistent results. */ BioLogica.Phenotype.numberOfChangesToReachPhenotype = function(testOrganism, targetOrganism, species, xAlleles) { var requiredChanges, testAlleles; testOrganism = BioLogica.Organism.ensureValidOrganism(testOrganism, species); targetOrganism = BioLogica.Organism.ensureValidOrganism(targetOrganism, species); requiredChanges = 0; if (testOrganism.sex !== targetOrganism.sex) { ++requiredChanges; testAlleles = testOrganism.getAlleleString(); if (testOrganism.sex === BioLogica.MALE && (xAlleles != null)) { testAlleles += "," + xAlleles; } testOrganism = new BioLogica.Organism(species, testAlleles, targetOrganism.sex); } return requiredChanges += BioLogica.Phenotype.numberOfAlleleChangesToReachPhenotype(testOrganism.phenotype.characteristics, targetOrganism.phenotype.characteristics, testOrganism.genetics.genotype.allAlleles, testOrganism.species, testOrganism.sex); }; /* Returns the number of separate allele changes required to make the phenotype of the organism characterized by 'testCharacterstics' match that of the organism characterized by 'targetCharacteristics'. Adapted from: @see https://github.com/concord-consortium/Geniverse-SproutCore/blob/master/frameworks/geniverse/controllers/match.js */ BioLogica.Phenotype.numberOfAlleleChangesToReachPhenotype = function(testCharacteristics, targetCharacteristics, testAlleles, species, sex) { var alleles, characteristicAlleles, gene, i, ii, j, jj, k, kk, l, len, moves, pathLength, possibleSolutions, possibleTraitGenes, shortestPathLength, solution, trait, traitRules; traitRules = species.traitRules; alleles = testAlleles; moves = 0; for (trait in traitRules) { if (traitRules.hasOwnProperty(trait) && (species.subTraits.indexOf(trait) < 0)) { if (testCharacteristics[trait] !== targetCharacteristics[trait]) { possibleTraitGenes = BioLogica.Genetics.collectAllGenesForCharacteristic(trait, targetCharacteristics[trait], species); possibleSolutions = traitRules[trait][targetCharacteristics[trait]]; characteristicAlleles = []; i = 0; ii = alleles.length; while (i < ii) { for (l = 0, len = possibleTraitGenes.length; l < len; l++) { gene = possibleTraitGenes[l]; if (BioLogica.Genetics.getGeneOfAllele(species, alleles[i]) === gene) { characteristicAlleles.push(alleles[i]); continue; } } i++; } if (sex === BioLogica.MALE && solutionsContainsAllele(possibleSolutions, "Y")) { characteristicAlleles.push("Y"); } shortestPathLength = 2e308; j = 0; jj = possibleSolutions.length; while (j < jj) { solution = possibleSolutions[j].slice(); pathLength = 0; k = 0; kk = characteristicAlleles.length; while (k < kk) { if (solution.indexOf(characteristicAlleles[k]) === -1) { pathLength++; } else { solution.splice(solution.indexOf(characteristicAlleles[k]), 1); } k++; } shortestPathLength = pathLength < shortestPathLength ? pathLength : shortestPathLength; j++; } moves += shortestPathLength; } } } return moves; }; BioLogica.Organism = (function() { function Organism(species1, alleles, sex1) { this.species = species1; this.sex = sex1; this.alleles = typeof alleles === "string" ? this.preProcessAlleleString(alleles) : alleles; this.genetics = new BioLogica.Genetics(this.species, this.alleles, this.sex); if (this.sex == null) { this.sex = this.genetics.genotype.sex; } this.resetPhenotype(); } Organism.prototype.getGenotype = function() { return this.genetics.genotype; }; Organism.prototype.resetPhenotype = function() { return this.phenotype = new BioLogica.Phenotype(this.genetics); }; /* For a given trait (a species-level property), returns this organism's characteristic. E.g. getCharacteristic("color") may return "green", getCharacteristic("horns") may return "no horns". */ Organism.prototype.getCharacteristic = function(trait) { return this.phenotype.characteristics[trait]; }; /* Returns an array containing all the org's characteristics, e.g. [Wings, No horns, ...] */ Organism.prototype.getAllCharacteristics = function() { return this.phenotype.allCharacteristics; }; Organism.prototype.getImageName = function() { return this.species.getImageName(this); }; Organism.prototype.getAlleleString = function() { return this.genetics.getAlleleString(); }; Organism.prototype.getAlleleStringForTrait = function(trait) { return this.genetics.getAlleleStringForTrait(trait); }; /* Creates n gametes, using crossover during meiosis by default . If only one gamete is requested, that gamete will be returned. Otherwise an array of gametes will be returned */ Organism.prototype.createGametes = function(n, performCrossover) { var gametes, i, l, ref; if (performCrossover == null) { performCrossover = true; } gametes = []; for (i = l = 0, ref = Math.floor(n / 4); 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { gametes = gametes.concat(this.genetics.performMeiosis(performCrossover).cells); } gametes = gametes.concat(this.genetics.performMeiosis(performCrossover).cells.slice(0, n % 4)); if (gametes.length === 1) { return gametes[0]; } else { return gametes; } }; Organism.prototype.createGametesWithCrossInfo = function(n, performCrossover) { var gametes, i, l, ref; if (performCrossover == null) { performCrossover = true; } gametes = []; for (i = l = 0, ref = Math.floor(n / 4); 0 <= ref ? l < ref : l > ref; i = 0 <= ref ? ++l : --l) { gametes = gametes.concat(this.genetics.performMeiosis(performCrossover)); } if (n % 4 !== 0) { gametes = gametes.concat(this.genetics.performMeiosis(performCrossover)); } return gametes; }; Organism.prototype.preProcessAlleleString = function(str) { return str.replace(/,+$/, ""); }; Organism.prototype.toString = function() { var alleles, sex; sex = this.sex === BioLogica.FEMALE ? "female" : "male"; alleles = this.genetics.genotype.allAlleles; return "Organism: {sex: " + sex + ", authored alleles: " + this.alleles + ", alleles: " + alleles; }; return Organism; })(); BioLogica.Organism.createOrganism = function(species, alleles, sex) { if (alleles == null) { alleles = ""; } if (sex == null) { sex = ExtMath.flip() ? BioLogica.FEMALE : BioLogica.MALE; } return new BioLogica.Organism(species, alleles, sex); }; BioLogica.Organism.createLiveOrganism = function(species, alleles, sex) { var org; if (alleles == null) { alleles = ""; } if (sex == null) { sex = ExtMath.flip() ? BioLogica.FEMALE : BioLogica.MALE; } org = new BioLogica.Organism(species, alleles, sex); org.species.makeAlive(org); return org; }; BioLogica.Organism.createFromGametes = function(species, motherGamete, fatherGamete) { var chromatidA, chromatidB, chromoName, chromosome, genotypeHash, i; for (i in fatherGamete) { chromosome = fatherGamete[i]; if (chromosome.side === "a") { chromosome.side = "b"; } } for (i in motherGamete) { chromosome = motherGamete[i]; if (chromosome.side === "b") { chromosome.side = "a"; } } if (fatherGamete["XY"].side === "y") { motherGamete["XY"].side = "x"; } else { motherGamete["XY"].side = "x1"; fatherGamete["XY"].side = "x2"; } genotypeHash = {}; for (chromoName in motherGamete) { if (!hasProp.call(motherGamete, chromoName)) continue; chromatidA = motherGamete[chromoName]; chromatidB = fatherGamete[chromoName]; genotypeHash[chromoName] = {}; genotypeHash[chromoName][chromatidA.side] = chromatidA.alleles; genotypeHash[chromoName][chromatidB.side] = chromatidB.alleles; } return new BioLogica.Organism(species, genotypeHash); }; BioLogica.Organism.ensureValidOrganism = function(orgOrDef, species) { if (orgOrDef.getAlleleString) { return orgOrDef; } return new BioLogica.Organism(species, orgOrDef.alleleString, orgOrDef.sex); }; BioLogica.Organism.numberOfBreedingMovesToReachOrganism = function(organism1, organism2, changeableAlleles1, changeableAlleles2, targetOrganism) { var allele1, allele2, i, ii, j, jj, moves, movesForSolution1, movesForSolution2, org1Alleles, org2Alleles, possibleSolutions, shortestPath, solution, solutionMoves, targetchars, trait, traitRules; moves = 0; org1Alleles = organism1.getAlleleString().split(',').map(function(a) { return a.split(':')[1]; }); org2Alleles = organism2.getAlleleString().split(',').map(function(a) { return a.split(':')[1]; }); targetchars = targetOrganism.phenotype.characteristics; traitRules = organism1.species.traitRules; for (trait in traitRules) { if (traitRules.hasOwnProperty(trait)) { possibleSolutions = traitRules[trait][targetchars[trait]]; shortestPath = 2e308; if (possibleSolutions && possibleSolutions.length) { i = 0; ii = possibleSolutions.length; while (i < ii) { solution = possibleSolutions[i]; movesForSolution1 = 0; movesForSolution2 = 0; j = 0; jj = solution.length; while (j < jj) { allele1 = solution[j]; allele2 = j % 2 === 0 ? solution[j + 1] : solution[j - 1]; solutionMoves = 0; if (org1Alleles.indexOf(allele1) === -1) { if (allele1 && (changeableAlleles1.indexOf(allele1) > -1 || changeableAlleles1.indexOf(allele1.toLowerCase()) > -1)) { solutionMoves++; } else { solutionMoves = 2e308; } } if (org2Alleles.indexOf(allele2) === -1) { if (allele2 && (changeableAlleles2.indexOf(allele2) > -1 || changeableAlleles2.indexOf(allele2.toLowerCase()) > -1)) { solutionMoves++; } else { solutionMoves = 2e308; } } if (j % 2 === 0) { movesForSolution1 += solutionMoves; } else { movesForSolution2 += solutionMoves; } j++; } shortestPath = Math.min(shortestPath, Math.min(movesForSolution1, movesForSolution2)); i++; } moves += shortestPath; } } } return moves; }; /* Breed two parents together. By default crossover will be used during meiosis */ BioLogica.breed = function(mother, father, crossover) { var gamete1, gamete2; gamete1 = mother.createGametes(1, crossover); gamete2 = father.createGametes(1, crossover); return BioLogica.Organism.createFromGametes(mother.species, gamete1, gamete2); }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.Dragon = { name: 'Dragon', chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['h', 's'], '2': ['w', 'l', 't'], 'XY': ['p', 'f', 'a', 'b'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { horns: { alleles: ['H', 'h', 'HU'], start: 5000000, length: 5333 }, scales: { alleles: ['S', 's'], start: 7500000, length: 7667 }, wings: { alleles: ['W', 'w', 'wd'], start: 2769231, length: 25128 }, legs: { alleles: ['L', 'l'], start: 4615385, length: 19230 }, tail: { alleles: ['T', 't', 'ta'], start: 6153846, length: 10256 }, plates: { alleles: ['P', 'p'], start: 2333333, length: 6000 }, fire: { alleles: ['F', 'f', 'fb'], start: 3033333, length: 26600 }, color1: { alleles: ['A', 'a', 'aw'], start: 4200000, length: 26133 }, color2: { alleles: ['B', 'b'], start: 6020000, length: 32200 } }, alleleLabelMap: { 'H': 'Horns', 'h': 'No horns', 'HU': 'Unicorn', 'S': 'No scales', 's': 'Scales', 'W': 'No wings', 'w': 'Wings', 'wd': 'Double wings', 'L': 'Legs', 'l': 'No legs', 'T': 'Fancy tail', 't': 'Plain tail', 'ta': 'Arrow tail', 'P': 'Plates', 'p': 'No plates', 'F': 'No fire', 'f': 'Fire', 'fb': 'Blue fire', 'A': 'Color A', 'a': 'Color a', 'aw': 'Color aw', 'B': 'Color B', 'b': 'Color b', 'Y': 'Y', '': '' }, traitRules: { 'horns': { 'Horns': [['H', 'H'], ['H', 'h']], 'No Horns': [['h', 'h']], 'Unicorn': [['HU', 'H'], ['HU', 'h'], ['HU', 'HU']] }, 'scales': { 'Scales': [['s', 's']], 'No Scales': [['S', 'S'], ['S', 's']] }, 'wings': { 'Double Wings': [['wd', 'wd'], ['wd', 'w']], 'Wings': [['w', 'w'], ['W', 'wd']], 'No Wings': [['W', 'W'], ['W', 'w']] }, 'legs': { 'Four Legs': [['L', 'L']], 'Two Legs': [['L', 'l']], 'No Legs': [['l', 'l']] }, 'tail': { 'Fancy Tail': [['T', 'T'], ['T', 't'], ['T', 'ta']], 'Plain Tail': [['t', 't'], ['t', 'ta']], 'Arrow Tail': [['ta', 'ta']] }, 'fire': { 'No Fire': [['F', 'F'], ['F', 'f'], ['F', 'fb'], ['F', 'Y']], 'Fire': [['f', 'f'], ['f', 'fb'], ['f', 'Y']], 'Blue Fire': [['fb', 'fb'], ['fb', 'Y']] }, 'color': { 'Green': [['A', 'B', 'Y'], ['A', 'b', 'Y']], 'Yellow': [['a', 'B', 'Y'], ['a', 'b', 'Y']], 'Red': [['A', 'A', 'B', 'B'], ['A', 'a', 'B', 'B']], 'Purple': [['a', 'a', 'B', 'B']], 'Brown': [['A', 'A', 'B', 'b'], ['A', 'A', 'b', 'b'], ['A', 'a', 'B', 'b'], ['A', 'a', 'b', 'b']], 'Blue': [['a', 'a', 'B', 'b'], ['a', 'a', 'b', 'b']], 'Albino': BioLogica.combinations([['aw'], ['A', 'a', 'aw'], ['B', 'b'], ['B', 'b']]).concat(BioLogica.combinations([['aw'], ['B', 'b'], ['Y']])) }, 'plates': { 'Big Plates': [['P', 'P']], 'Little Plates': [['P', 'p'], ['P', 'Y']], 'No Plates': [['p', 'p'], ['p', 'Y']] }, 'liveliness': { 'Alive': [['B', 'B'], ['B', 'b'], ['B', 'Y']], 'Dead': [['b', 'b'], ['b', 'Y']] } }, /* Uses an external engine's sprite sheets to generate the images. */ getImageName: function(org) { return void 0; }, /* Makes sure the organism has at least one big B */ makeAlive: function(org) { var chromosome, chromosome1, chromosome2; if (org.getCharacteristic('liveliness') === 'Dead') { if (org.sex === BioLogica.MALE) { chromosome = org.getGenotype().chromosomes['XY']['x']; } else { chromosome1 = org.getGenotype().chromosomes['XY']['x1']; chromosome2 = org.getGenotype().chromosomes['XY']['x2']; if (indexOf.call(chromosome1.alleles, 'b') >= 0) { if (indexOf.call(chromosome2.alleles, 'b') >= 0) { chromosome = ExtMath.flip() ? chromosome1 : chromosome2; } else { chromosome = chromosome1; } } else { chromosome = chromosome2; } } org.getGenotype().replaceAllele(chromosome, 'b', 'B'); return org.resetPhenotype(); } } }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.Drake = { name: "Drake", chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['t', 'm', 'w', 'h'], '2': ['c', 'b', 'fl', 'hl', 'a'], 'XY': ['d', 'rh', 'bog'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { tail: { alleles: ['T', 'Tk', 't'], start: 10000000, length: 10584 }, metallic: { alleles: ['M', 'm'], start: 20000000, length: 259610 }, wings: { alleles: ['W', 'w'], start: 70000000, length: 9094 }, horns: { alleles: ['H', 'h'], start: 85000000, length: 19421 }, color: { alleles: ['C', 'c'], start: 15000000, length: 64572 }, black: { alleles: ['B', 'b'], start: 25000000, length: 17596 }, forelimbs: { alleles: ['Fl', 'fl'], start: 80000000, length: 122234 }, hindlimbs: { alleles: ['Hl', 'hl'], start: 85000000, length: 6371 }, armor: { alleles: ['A1', 'A2', 'a'], start: 90000000, length: 425156 }, dilute: { alleles: ['D', 'd'], start: 20000000, length: 152673 }, bogbreath: { alleles: ['Bog', 'bog'], start: 22000000, length: 199642 }, nose: { alleles: ['Rh', 'rh'], start: 60000000, length: 2950 } }, alleleLabelMap: { 'T': 'Long tail', 'Tk': 'Kinked tail', 't': 'Short tail', 'M': 'Shiny', 'm': 'Dull', 'W': 'Wings', 'w': 'Wingless', 'H': 'Hornless', 'h': 'Horns', 'C': 'Color', 'c': 'Albino', 'Fl': 'Arms', 'fl': 'Armless', 'Hl': 'Legs', 'hl': 'Legless', 'A1': "Full armor", 'A2': "Partial armor", 'a': "No armor", 'B': 'Gray', 'b': 'Orange', 'D': 'Deep', 'd': 'Faded', 'Rh': 'Spiked', 'rh': 'Spikeless', 'Bog': 'Normal', 'bog': 'Bog breath', 'Y': 'Y', '': '' }, traitRules: { "armor": { "Five armor": [["A1", "A1"], ["A1", "A2"]], "Three armor": [["A1", "a"], ["A2", "A2"]], "One armor": [["A2", "a"]], "No armor": [["a", "a"]] }, "tail": { "Long tail": [["T", "T"], ["T", "Tk"], ["T", "t"]], "Kinked tail": [["Tk", "Tk"], ["Tk", "t"]], "Short tail": [["t", "t"]] }, "forelimbs": { "Forelimbs": [["Fl", "Fl"], ["Fl", "fl"]], "No forelimbs": [["fl", "fl"]] }, "hindlimbs": { "Hindlimbs": [["Hl", "Hl"], ["Hl", "hl"]], "No hindlimbs": [["hl", "hl"]] }, "horns": { "Hornless": [["H", "H"], ["H", "h"]], "Horns": [["h", "h"]] }, "nose spike": { "Nose spike": BioLogica.combinations([["Rh"], ["Rh", "rh", "Y"]]), "No nose spike": [["rh", "rh"], ["rh", "Y"]] }, "wings": { "Wings": [["W", "W"], ["W", "w"]], "No wings": [["w", "w"]] }, "color": { "Frost": [["c", "c"]], "Steel": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["B"], ["B", "b"], ["D"], ["D", "d", "Y"]]), "Copper": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["b"], ["b"], ["D"], ["D", "d", "Y"]]), "Silver": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["B"], ["B", "b"], ["d"], ["d", "Y"]]), "Gold": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["b"], ["b"], ["d"], ["d", "Y"]]), "Charcoal": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["B"], ["B", "b"], ["D"], ["D", "d", "Y"]]), "Lava": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["b"], ["b"], ["D"], ["D", "d", "Y"]]), "Ash": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["B"], ["B", "b"], ["d"], ["d", "Y"]]), "Sand": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["b"], ["b"], ["d"], ["d", "Y"]]) }, "health": { "Bog breath": [['bog', 'bog'], ['bog', 'Y']], "Healthy": [['Bog', 'Bog'], ['Bog', 'bog'], ['Bog', 'Y']] }, "metallic": { "Shiny": [["M", "M"], ["M", "m"]], "Dull": [["m", "m"]] }, "colored": { "Colored": [["C", "C"], ["C", "c"]], "Albino": [["c", "c"]] }, "black": { "Gray": [["B", "B"], ["B", "b"]], "Orange": [["b", "b"]] }, "dilute": { "Deep": [["D", "D"], ["D", "d"], ["D", "Y"]], "Faded": [["d", "d"], ["d", "Y"]] } }, subTraits: ["metallic", "colored", "black", "dilute"], /* Gets the image name based on the organism's characteristics. Requires the BioLogica.js library, and for org to be a BioLogica.js organism */ getImageName: function(org) { var filename, limbs, trait, traitColor; trait = function(trait) { return org.getCharacteristic(trait); }; if (trait("liveliness") === "Dead") { return "dead-drake.png"; } filename = ""; traitColor = trait("color"); if (traitColor === "Silver") { traitColor = "Argent"; } else if (traitColor === "Lava") { traitColor = "Earth"; } else if (traitColor === "Ash") { traitColor = "Dust"; } filename += traitColor.toLowerCase().substring(0, 2) + "_"; filename += org.sex === BioLogica.FEMALE ? "f_" : "m_"; filename += trait("wings") === "Wings" ? "wing_" : "noWing_"; limbs = ""; if (trait("forelimbs") === "Forelimbs") { if (trait("hindlimbs") === "Hindlimbs") { limbs = "allLimb_"; } else { limbs = "fore_"; } } else if (trait("hindlimbs") === "Hindlimbs") { limbs = "hind_"; } else { limbs = "noLimb_"; } filename += limbs; filename += (function() { switch (trait("armor")) { case "Five armor": return "a5_"; case "Three armor": return "a3_"; case "One armor": return "a1_"; default: return "a0_"; } })(); filename += (function() { switch (trait("tail")) { case "Long tail": return "flair_"; case "Kinked tail": return "kink_"; default: return "short_"; } })(); filename += trait("horns") === "Horns" ? "horn_" : "noHorn_"; filename += trait("nose spike") === "Nose spike" ? "rostral_" : "noRostral_"; filename += trait("health") === "Bog breath" ? "bogbreath" : "healthy"; return filename += ".png"; }, /* */ makeAlive: function(org) {} }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.GenivilleDrake = { name: "GenivilleDrake", chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['t', 'm', 'w', 'h'], '2': ['c', 'b', 'fl', 'hl', 'a'], 'XY': ['d', 'rh', 'bog'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { tail: { alleles: ['T', 'Tk', 't'], start: 10000000, length: 10584 }, metallic: { alleles: ['M', 'm'], start: 20000000, length: 259610 }, wings: { alleles: ['W', 'w'], start: 70000000, length: 9094 }, horns: { alleles: ['H', 'h'], start: 85000000, length: 19421 }, color: { alleles: ['C', 'c'], start: 15000000, length: 64572 }, black: { alleles: ['B', 'b'], start: 25000000, length: 17596 }, forelimbs: { alleles: ['Fl', 'fl'], start: 80000000, length: 122234 }, hindlimbs: { alleles: ['Hl', 'hl'], start: 85000000, length: 6371 }, armor: { alleles: ['A', 'a'], start: 90000000, length: 425156 }, dilute: { alleles: ['D', 'd', 'dl'], start: 20000000, length: 152673 }, bogbreath: { alleles: ['Bog', 'bog'], start: 22000000, length: 199642 }, nose: { alleles: ['Rh', 'rh'], start: 60000000, length: 2950 } }, alleleLabelMap: { 'T': 'Long tail', 'Tk': 'Kinked tail', 't': 'Short tail', 'M': 'Metallic', 'm': 'Nonmetallic', 'W': 'Wings', 'w': 'No wings', 'H': 'No horns', 'h': 'Horns', 'C': 'Colored', 'c': 'Colorless', 'Fl': 'Forelimbs', 'fl': 'No forelimbs', 'Hl': 'Hindlimbs', 'hl': 'No hindlimbs', 'A': 'Armor', 'a': 'No armor', 'B': 'Black', 'b': 'Brown', 'D': 'Full color', 'd': 'Dilute color', 'dl': 'dl', 'Rh': 'Nose spike', 'rh': 'No nose spike', 'Bog': 'Healthy', 'bog': 'Bog breath', 'Y': 'Y', '': '' }, traitRules: { "armor": { "Five armor": [["A", "A"]], "Three armor": [["A", "a"]], "No armor": [["a", "a"]] }, "tail": { "Long tail": [["T", "T"], ["T", "Tk"], ["T", "t"]], "Kinked tail": [["Tk", "Tk"], ["Tk", "t"]], "Short tail": [["t", "t"]] }, "forelimbs": { "Forelimbs": [["Fl", "Fl"], ["Fl", "fl"]], "No forelimbs": [["fl", "fl"]] }, "hindlimbs": { "Hindlimbs": [["Hl", "Hl"], ["Hl", "hl"]], "No hindlimbs": [["hl", "hl"]] }, "horns": { "Hornless": [["H", "H"], ["H", "h"]], "Horns": [["h", "h"]] }, "nose spike": { "Nose spike": BioLogica.combinations([["Rh"], ["Rh", "rh", "Y"]]), "No nose spike": [["rh", "rh"], ["rh", "Y"]] }, "wings": { "Wings": [["W", "W"], ["W", "w"]], "No wings": [["w", "w"]] }, "color": { "Frost": [["c", "c"]], "Steel": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["B"], ["B", "b"], ["D"], ["D", "d", "dl", "Y"]]), "Copper": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["b"], ["b"], ["D"], ["D", "d", "dl", "Y"]]), "Silver": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["B"], ["B", "b"], ["d", "dl"], ["d", "dl", "Y"]]), "Gold": BioLogica.combinations([["C"], ["C", "c"], ["M"], ["M", "m"], ["b"], ["b"], ["d", "dl"], ["d", "dl", "Y"]]), "Charcoal": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["B"], ["B", "b"], ["D"], ["D", "d", "dl", "Y"]]), "Lava": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["b"], ["b"], ["D"], ["D", "d", "dl", "Y"]]), "Ash": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["B"], ["B", "b"], ["d", "dl"], ["d", "dl", "Y"]]), "Sand": BioLogica.combinations([["C"], ["C", "c"], ["m"], ["m"], ["b"], ["b"], ["d", "dl"], ["d", "dl", "Y"]]) }, "health": { "Bog breath": [['bog', 'bog'], ['bog', 'Y']], "Healthy": [['Bog', 'Bog'], ['Bog', 'bog'], ['Bog', 'Y']] }, "liveliness": { "Alive": BioLogica.combinations([["D", "d"], ["D", "d", "dl", "Y"]]), "Dead": [["dl", "dl"], ["dl", "Y"]] } }, /* Gets the image name based on the organism's characteristics. Requires the BioLogica.js library, and for org to be a BioLogica.js organism */ getImageName: function(org) { var filename, limbs, trait, traitColor; trait = function(trait) { return org.getCharacteristic(trait); }; if (trait("liveliness") === "Dead") { return "dead-GenivilleDrake.png"; } filename = ""; traitColor = trait("color"); if (traitColor === "Silver") { traitColor = "Argent"; } else if (traitColor === "Lava") { traitColor = "Earth"; } else if (traitColor === "Ash") { traitColor = "Dust"; } filename += traitColor.toLowerCase().substring(0, 2) + "_"; filename += org.sex === BioLogica.FEMALE ? "f_" : "m_"; filename += trait("wings") === "Wings" ? "wing_" : "noWing_"; limbs = ""; if (trait("forelimbs") === "Forelimbs") { if (trait("hindlimbs") === "Hindlimbs") { limbs = "allLimb_"; } else { limbs = "fore_"; } } else if (trait("hindlimbs") === "Hindlimbs") { limbs = "hind_"; } else { limbs = "noLimb_"; } filename += limbs; filename += (function() { switch (trait("armor")) { case "Five armor": return "a5_"; case "Three armor": return "a3_"; default: return "a0_"; } })(); filename += (function() { switch (trait("tail")) { case "Long tail": return "flair_"; case "Kinked tail": return "kink_"; default: return "short_"; } })(); filename += trait("horns") === "Horns" ? "horn_" : "noHorn_"; filename += trait("nose spike") === "Nose spike" ? "rostral_" : "noRostral_"; filename += trait("health") === "Bog breath" ? "bogbreath" : "healthy"; return filename += ".png"; }, /* */ makeAlive: function(org) { var chromsome, replacementAllele, xChromoName; if (org.getCharacteristic("liveliness") === "Dead") { xChromoName = org.sex === BioLogica.MALE ? "x" : ExtMath.flip() ? "x1" : "x2"; chromsome = org.getGenotype().chromosomes["XY"][xChromoName]; replacementAllele = ExtMath.flip() ? "D" : "d"; org.getGenotype().replaceAllele(chromsome, "dl", replacementAllele); return org.resetPhenotype(); } } }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.GGDrake = { name: 'GGDrake', chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['T3', 'M2', 'W2', 'A2'], '2': ['H2', 'C2', 'G2', 'S2'], 'XY': ['D2', 'F2'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { tail: { alleles: ['T1', 'T2', 'T3'], start: 10000000, length: 10584 }, metallic: { alleles: ['M1', 'M2'], start: 20000000, length: 259610 }, wings: { alleles: ['W1', 'W2'], start: 70000000, length: 9094 }, armor: { alleles: ['A1', 'A2'], start: 80000000, length: 122234 }, horns: { alleles: ['H1', 'H2'], start: 15000000, length: 19421 }, color: { alleles: ['C1', 'C2'], start: 45000000, length: 64572 }, green: { alleles: ['G1', 'G2'], start: 55000000, length: 17596 }, spikes: { alleles: ['S1', 'S2'], start: 90000000, length: 6371 }, dilute: { alleles: ['D1', 'D2'], start: 20000000, length: 152673 }, firebreathing: { alleles: ['F1', 'F2'], start: 60000000, length: 1000 } }, alleleLabelMap: { 'T1': 'Long tail', 'T2': 'Kinked tail', 'T3': 'Short tail', 'M1': 'Metallic', 'M2': 'Nonmetallic', 'W1': 'Wings', 'W2': 'No wings', 'H1': 'No horns', 'H2': 'Horns', 'C1': 'Colored', 'C2': 'Colorless', 'A1': 'No armor', 'A2': 'Armor', 'S1': 'Spikes wide', 'S2': 'Spikes narrow', 'G1': 'Green', 'G2': 'Purple', 'D1': 'Full color', 'D2': 'Dilute color', 'F1': 'No fire breathing', 'F2': 'Fire breathing', 'Y': 'Y', '': '' }, traitRules: { 'tail': { 'Long tail': [['T1', 'T1'], ['T1', 'T2'], ['T1', 'T3']], 'Kinked tail': [['T2', 'T2'], ['T2', 'T3']], 'Short tail': [['T3', 'T3']] }, 'wings': { 'Wings': [['W1', 'W1'], ['W1', 'W2']], 'No wings': [['W2', 'W2']] }, 'horns': { 'Reverse horns': [['H1', 'H1'], ['H1', 'H2']], 'Forward horns': [['H2', 'H2']] }, 'armor': { 'No armor': [['A1', 'A1'], ['A1', 'A2']], 'Armor': [['A2', 'A2']] }, 'spikes': { 'Wide spikes': [['S1', 'S1']], 'Medium spikes': [['S1', 'S2']], 'Narrow spikes': [['S2', 'S2']] }, 'fire breathing': { 'No fire breathing': [['F1']], 'Fire breathing': [['F2', 'F2'], ['F2', 'Y']] }, 'color': { 'Green': [['C1', 'G1', 'D1']], 'Blue': [['C1', 'G1', 'D2', 'D2'], ['C1', 'G1', 'D2', 'Y']], 'Purple': [['C1', 'G2', 'G2', 'D1']], 'Red': [['C1', 'G2', 'G2', 'D2', 'D2'], ['C1', 'G2', 'G2', 'D2', 'Y']], 'Albino': [['C2', 'C2']] }, 'metallic': { 'Metallic': [['M1']], 'Nonmetallic': [['M2', 'M2']] } }, /* GGDrakes are pieced together by sprites */ getImageName: function(org) {}, /* GGDrakes have no lethal characteristics */ makeAlive: function(org) {} }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.GGTournamentDrake = { name: 'GGTournamentDrake', chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['T3', 'W2'], '2': ['H2', 'B2', 'G2', 'S2'], 'XY': ['N2', 'F2'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { tail: { alleles: ['T1', 'T2', 'T3', 'T4'], start: 10000000, length: 10584 }, wings: { alleles: ['W1', 'W2'], start: 70000000, length: 9094 }, horns: { alleles: ['H1', 'H2', 'H3'], start: 15000000, length: 19421 }, backfin: { alleles: ['B1', 'B2'], start: 45000000, length: 64572 }, green: { alleles: ['G1', 'G2'], start: 55000000, length: 17596 }, spikes: { alleles: ['S1', 'S2'], start: 90000000, length: 6371 }, neckpattern: { alleles: ['N1', 'N2'], start: 40000000, length: 14003 }, firebreathing: { alleles: ['F1', 'F2'], start: 60000000, length: 1000 } }, alleleLabelMap: { 'T1': 'Long tail', 'T2': 'Kinked tail', 'T3': 'Short tail', 'T4': 'Fat tail', 'W1': 'Wings', 'W2': 'No wings', 'H1': 'No horns', 'H2': 'Horns', 'H3': 'Upward horns', 'B1': 'Small fin', 'B2': 'Large fin', 'G1': 'Green', 'G2': 'Purple', 'S1': 'Spikes wide', 'S2': 'Spikes narrow', 'F1': 'No fire breathing', 'F2': 'Fire breathing', 'N1': 'Spotted neck', 'N2': 'Striped neck', 'Y': 'Y', '': '' }, traitRules: { 'tail': { 'Long tail': [['T1', 'T1'], ['T1', 'T2'], ['T1', 'T3'], ['T1', 'T4']], 'Kinked tail': [['T2', 'T2'], ['T2', 'T3'], ['T2', 'T4']], 'Short tail': [['T3', 'T3'], ['T3', 'T4']], 'Fat tail': [['T4', 'T4']] }, 'wings': { 'Wings': [['W1', 'W1'], ['W1', 'W2']], 'No wings': [['W2', 'W2']] }, 'spikes': { 'Wide spikes': [['S1', 'S1']], 'Medium spikes': [['S1', 'S2']], 'Narrow spikes': [['S2', 'S2']] }, 'horns': { 'Reverse horns': [['H1', 'H1'], ['H1', 'H2'], ['H1', 'H3']], 'Forward horns': [['H2', 'H2'], ['H2', 'H3']], 'Upward horns': [['H3', 'H3']] }, 'fire breathing': { 'No fire breathing': [['F1']], 'Fire breathing': [['F2', 'F2'], ['F2', 'Y']] }, 'fin': { 'Small fin': [['B1', 'B1']], 'Medium fin': [['B1', 'B2']], 'Large fin': [['B2', 'B2']] }, 'neckpattern': { 'Spotted neck': [['N1', 'N1'], ['N1', 'N2'], ['N1', 'Y']], 'Striped neck': [['N2', 'N2'], ['N2', 'Y']] }, 'color': { 'Green': [['G1', 'G1'], ['G1', 'G2']], 'Purple': [['G2', 'G2']] } }, /* GGDrakes are pieced together by sprites */ getImageName: function(org) {}, /* GGDrakes have no lethal characteristics */ makeAlive: function(org) {} }; BioLogica.Species = BioLogica.Species || {}; BioLogica.Species.GGLizard = { name: 'GGLizard', chromosomeNames: ['1', '2', 'XY'], chromosomeGeneMap: { '1': ['R3', 'M2', 'W2', 'E2'], '2': ['Q2', 'C2', 'G2', 'L2'], 'XY': ['D2', 'V2'] }, chromosomesLength: { '1': 100000000, '2': 100000000, 'XY': 70000000 }, geneList: { ridges: { alleles: ['R1', 'R2', 'R3'], start: 10000000, length: 10584 }, metallic: { alleles: ['M1', 'M2'], start: 20000000, length: 259610 }, wings: { alleles: ['W1', 'W2'], start: 70000000, length: 9094 }, armor: { alleles: ['E1', 'E2'], start: 80000000, length: 122234 }, quill: { alleles: ['Q1', 'Q2'], start: 15000000, length: 19421 }, color: { alleles: ['C1', 'C2'], start: 45000000, length: 64572 }, green: { alleles: ['G1', 'G2'], start: 55000000, length: 17596 }, legs: { alleles: ['L1', 'L2'], start: 90000000, length: 6371 }, dilute: { alleles: ['D1', 'D2'], start: 20000000, length: 152673 }, venomous: { alleles: ['V1', 'V2'], start: 60000000, length: 1000 } }, alleleLabelMap: { 'R1': 'Squared ridges', 'R2': 'Rounded ridges', 'R3': 'Pointy ridges', 'M1': 'Metallic', 'M2': 'Nonmetallic', 'W1': 'Wings', 'W2': 'No wings', 'Q1': 'Quill 1', 'Q2': 'Quill 2', 'C1': 'Colored', 'C2': 'Colorless', 'E1': 'No ear flaps', 'E2': 'Ear flaps', 'L1': 'Long legs', 'L2': 'Short legs', 'G1': 'Green', 'G2': 'Purple', 'D1': 'Full color', 'D2': 'Dilute color', 'V1': 'Not venomous', 'V2': 'Venomous', 'Y': 'Y', '': '' }, traitRules: { 'ridges': { 'Squared ridges': [['R1', 'R1'], ['R1', 'R2'], ['R1', 'R3']], 'Rounded ridges': [['R2', 'R2'], ['R2', 'R3']], 'Pointy ridges': [['R3', 'R3']] }, 'wings': { 'Wings': [['W1']], 'No wings': [['W2', 'W2']] }, 'quill': { 'Quill 1': [['Q1']], 'Quill 2': [['Q2', 'Q2']] }, 'ear flaps': { 'No ear flaps': [['E1']], 'Ear flaps': [['E2', 'E2']] }, 'legs': { 'Long legs': [['L1', 'L1']], 'Medium legs': [['L1', 'L2']], 'Short legs': [['L2', 'L2']] }, 'venomous': { 'Non venomous': [['V1']], 'Venomous': [['V2', 'V2'], ['V2', 'Y']] }, 'color': { 'Green': [['G1', 'D1']], 'Blue': [['G1', 'D2', 'D2'], ['G1', 'D2', 'Y']], 'Purple': [['G2', 'G2', 'D1']], 'Red': [['G2', 'G2', 'D2', 'D2'], ['G2', 'G2', 'D2', 'Y']] }, 'metallic': { 'Metallic': [['M1']], 'Nonmetallic': [['M2', 'M2']] } }, /* GGLizards are pieced together by sprites */ getImageName: function(org) {}, /* GGLizards have no lethal characteristics */ makeAlive: function(org) {} }; }).call(this);