Skip to content

Instantly share code, notes, and snippets.

@qgustavor
Created May 7, 2024 16:14
Show Gist options
  • Save qgustavor/478e9c4db34244b200ac27e268546d46 to your computer and use it in GitHub Desktop.
Save qgustavor/478e9c4db34244b200ac27e268546d46 to your computer and use it in GitHub Desktop.
Population model to simulate what could happen if two female woman could have female children together and children had 95% chance of inheriting their parents' sexuality
// Function to interpolate missing rates
function interpolateRate(rates, age) {
const surroundingRates = Object.entries(rates)
.filter(([key]) => Number(key) <= age)
.sort((a, b) => Number(a[0]) - Number(b[0])) // Sort by age (ascending)
if (surroundingRates.length === 0) {
// No rates available, return 0 (adjust as needed)
return 0
}
const closestRate = surroundingRates.pop() // Get the closest rate (highest age)
const closestAge = Number(closestRate[0])
const closestValue = closestRate[1]
if (age === closestAge) {
return closestValue
}
// Linear interpolation between closest rates (adjust for different interpolation methods if needed)
const previousRate = surroundingRates.length > 0 ? surroundingRates.pop()[1] : closestValue
const ageDiff = closestAge - (surroundingRates.length > 0 ? Number(surroundingRates.pop()[0]) : 0)
const rateDiff = closestValue - previousRate
const ageRatio = (age - closestAge) / ageDiff
return closestValue - (ageRatio * rateDiff)
}
function simulatePopulation (options = {}) {
const {
initialMales = 1000,
initialFemales = 1000,
generations = 1000,
mortalityRates = { 0: 0.001, 20: 0.002, 80: 0.07, 150: 1 },
fertilityRates = { 0: 0, 10: 0, 15: 0.1, 20: 0.4, 30: 0.5, 40: 0.2, 50: 0.1, 60: 0 },
sexualityInheritRate = 0.95,
newCoupleRate = 0.3
} = options
const population = []
// Initialize population with individuals and age
for (let i = 0; i < initialMales; i++) {
population.push({ gender: 'male', age: 0, sexuality: 'heterosexual' })
}
for (let i = 0; i < initialFemales; i++) {
population.push({ gender: 'female', age: 0, sexuality: 'heterosexual' })
}
for (let generation = 0; generation < generations; generation++) {
// Age individuals
for (let j = 0; j < population.length; j++) {
population[j].age++
}
// Form couples (considering sexuality)
const singles = population.filter((individual) => !individual.partner)
const potentialCouples = Math.floor(singles.length / 2)
const newCouples = Math.min(potentialCouples, Math.floor(potentialCouples * newCoupleRate))
for (let j = 0; j < newCouples; j++) {
let maleIndex, femaleIndex
let attempts = 0 // Counter for loop attempts
do {
attempts++
maleIndex = Math.floor(Math.random() * singles.length)
femaleIndex = (maleIndex + 1) % singles.length
} while (
singles[maleIndex].gender === singles[femaleIndex] ||
singles[maleIndex] === singles[femaleIndex] ||
attempts > 10
)
// If attempts limit is reached, skip remaining partner assignments
if (attempts > 10) break
if (singles[maleIndex].gender === 'male' && singles[femaleIndex].gender === 'female') {
// Opposite sex couple, both can be potential partners
if (singles[maleIndex].sexuality === 'heterosexual' && singles[femaleIndex].sexuality === 'heterosexual') {
singles[maleIndex].partner = singles[femaleIndex]
singles[femaleIndex].partner = singles[maleIndex]
}
} else if (singles[maleIndex].gender === 'female' && singles[femaleIndex].gender === 'female') {
// Same-sex female couple (both homosexual for simplicity)
if (singles[maleIndex].sexuality === 'homosexual' && singles[femaleIndex].sexuality === 'homosexual') {
singles[maleIndex].partner = singles[femaleIndex]
singles[femaleIndex].partner = singles[maleIndex]
}
}
}
// Handle births with age-specific fertility and gender determination
const couples = population.filter((individual) => individual.partner).map(individual => [individual, individual.partner])
const newBirths = Math.floor(
couples.reduce((totalBirths, couple) => {
const fertilityRate = interpolateRate(fertilityRates, couple[0].age)
return totalBirths + fertilityRate
}, 0)
)
for (let j = 0; j < newBirths; j++) {
const couple = couples[Math.floor(Math.random() * couples.length)]
const childGender = couple[0].gender === 'female' && couple[1].gender === 'female'
? 'female' // Female-only couple, child is always female (XX)
: Math.random() < 0.5 ? 'male' : 'female' // Random gender for heterosexual couples
population.push({
gender: childGender,
partner: null,
age: 0,
sexuality: Math.random() < sexualityInheritRate // Child inherits parents' sexuality with 80% chance
? couple[0].sexuality || couple[1].sexuality // Handle potential missing sexuality
: couple[0].sexuality === 'heterosexual' ? 'homosexual' : 'heterosexual', // Opposite of parents'
})
}
// Handle deaths with age-specific mortality
const deceased = []
for (let j = 0; j < population.length; j++) {
const individual = population[j]
const mortalityRate = interpolateRate(mortalityRates, individual.age)
if (Math.random() < mortalityRate) {
deceased.push(individual)
if (individual.partner) {
individual.partner.partner = null // Set partner to null if deceased has one
}
}
}
for (const individual of deceased) {
population.splice(population.indexOf(individual), 1)
}
const totalPopulation = population.length
const malePopulation = population.filter(e => e.gender === 'male').length
const femalePopulation = population.filter(e => e.gender === 'female').length
console.log({ generation, totalPopulation, malePopulation, femalePopulation })
}
return population
}
// Example usage
simulatePopulation()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment