Skip to main content

Allelia

Allelia is a lightweight genetics library for games and simulations.

It allows you to define genes and alleles, create parent genomes, and generate child genetics using direct inheritance, blending, and mutation.

Allelia is designed to be flexible rather than biologically accurate. The library handles the inheritance system while your game or simulation determines what the genetic information represents.

Features

  • Continuous genes
  • Discrete genes
  • Direct inheritance
  • Blended inheritance
  • Mutation
  • Genotypes and phenotypes
  • Custom inheritance functions
  • Custom expression functions
  • Custom mutation functions
  • One or more parents
  • Numeric, tuple, list, and dictionary blending

Basic Concepts

Gene

A Gene defines how a particular genetic trait behaves.

from allelia import Gene

eye_color = Gene(
	"eye_color",
	"continuous"
)

Allelia currently supports two gene types:

"continuous"
"discrete"

Continuous genes can blend their alleles together.

Discrete genes select individual alleles without blending them.


Genome

A Genome stores the alleles belonging to an individual.

from allelia import Genome

parent = Genome()

parent.set(
	"eye_color",
	[
		(110, 70, 40),
		(70, 130, 180)
	]
)

You can retrieve the genotype with:

parent.get(
	"eye_color"
)

You can also check whether a genome contains a gene:

parent.has(
	"eye_color"
)

Creating an Allelia System

Create an Allelia instance and register the genes used by your system.

from allelia import Allelia, Gene

allelia = Allelia()

allelia.add_gene(
	Gene(
		"eye_color",
		"continuous"
	)
)

By default, inheritance probabilities are:

Direct Inheritance: 80%
Blending:           18%
Mutation:            2%

You can change these when creating the system:

allelia = Allelia(
	direct_inheritance=0.80,
	blending=0.18,
	mutation=0.02
)

The values are treated as relative weights, so they do not have to add up to exactly 1.0.


Creating Parents

Each parent is represented by a Genome.

parent_1 = Genome()

parent_1.set(
	"eye_color",
	[
		(110, 70, 40),
		(70, 130, 180)
	]
)


parent_2 = Genome()

parent_2.set(
	"eye_color",
	[
		(80, 130, 75),
		(130, 145, 150)
	]
)

In this example, each parent has two eye color alleles.


Creating a Child

Pass the parent genomes to:

allelia.create_child()

For example:

child = allelia.create_child(
	parent_1,
	parent_2
)

Each parent contributes one allele for every gene that parent contains.

The resulting child stores those contributions as its genotype.

print(
	child.get("eye_color")
)

Phenotypes

A genotype contains an individual's alleles.

A phenotype is the allele that is expressed.

Use:

allelia.express()

to generate a phenotype from a genome.

child_phenotype = allelia.express(
	child
)

print(
	child_phenotype["eye_color"]
)

By default, Allelia randomly selects an allele from the genotype for expression.

Custom expression rules can also be supplied to a gene.


Continuous Genes

Continuous genes allow values to be blended.

For example:

Gene(
	"height",
	"continuous"
)

Allelia can blend numerical alleles:

170
190

into an intermediate value.

Continuous genes can also contain more complicated numerical structures.

For example, RGB colors:

[
	(110, 70, 40),
	(70, 130, 180)
]

can produce intermediate colors.


Discrete Genes

Discrete genes represent traits that should remain separate rather than being mathematically blended.

For example:

allelia.add_gene(
	Gene(
		"ear_type",
		"discrete"
	)
)

A genome might contain:

parent.set(
	"ear_type",
	[
		"round",
		"pointed"
	]
)

Allelia selects one of the available alleles when that parent contributes the gene.


Blending

Allelia includes a recursive blend() function.

It can blend:

  • Numbers
  • Tuples
  • Lists
  • Dictionaries

For example:

from allelia.allelia import blend

result = blend(
	100,
	200,
	0.5
)

print(result)

Result:

150.0

The ratio determines how much each value contributes.

blend(
	100,
	200,
	0.25
)

uses:

75% of the first value
25% of the second value

The same system works recursively with tuples:

blend(
	(100, 50),
	(200, 100),
	0.5
)

and dictionaries:

blend(
	{
		"x": 100,
		"y": 50
	},
	{
		"x": 200,
		"y": 100
	},
	0.5
)

This makes continuous genes useful for more than simple numbers.


Mutation

Mutation behavior can be defined separately for each gene.

Create a function that accepts:

allele
gene

For example:

import random


def mutate_color(
	allele,
	gene
):
	return tuple(
		max(
			0,
			min(
				255,
				value + random.randint(-20, 20)
			)
		)
		for value in allele
	)

Then assign it to the gene:

allelia.add_gene(
	Gene(
		"eye_color",
		"continuous",
		mutate=mutate_color
	)
)

When mutation is selected during inheritance, Allelia generates the inherited allele and passes it to your mutation function.

If no mutation function is supplied, the allele is returned unchanged.

This allows the application using Allelia to determine what mutation means for its particular data.


Custom Expression

You can override the default phenotype behavior.

An expression function receives:

genotype
gene

Example:

def express_first(
	genotype,
	gene
):
	return genotype[0]

Then:

Gene(
	"eye_color",
	"continuous",
	express=express_first
)

Allelia will use your function instead of randomly selecting an allele.


Custom Inheritance

Genes can also provide their own inheritance behavior.

An inheritance function receives:

genotype
gene

For example:

def inherit_first(
	genotype,
	gene
):
	return genotype[0]

Then:

Gene(
	"special_gene",
	"continuous",
	inherit=inherit_first
)

This allows individual genes to override Allelia's default contribution behavior.


More Than Two Parents

Allelia does not require exactly two parents.

create_child() accepts one or more parent genomes.

For example:

child = allelia.create_child(
	parent_1,
	parent_2,
	parent_3
)

Each parent that contains a particular gene contributes one allele to the child's genotype for that gene.

This allows Allelia to support fictional reproductive systems without requiring special handling inside the library.


Complete Example

from allelia import Allelia, Gene, Genome


allelia = Allelia()


allelia.add_gene(
	Gene(
		"eye_color",
		"continuous"
	)
)


parent_1 = Genome()

parent_1.set(
	"eye_color",
	[
		(110, 70, 40),
		(70, 130, 180)
	]
)


parent_2 = Genome()

parent_2.set(
	"eye_color",
	[
		(80, 130, 75),
		(130, 145, 150)
	]
)


child = allelia.create_child(
	parent_1,
	parent_2
)


phenotype = allelia.express(
	child
)


print(
	"Parent 1:",
	parent_1.get("eye_color")
)

print(
	"Parent 2:",
	parent_2.get("eye_color")
)

print(
	"Child:",
	child.get("eye_color")
)

print(
	"Expressed Color:",
	phenotype["eye_color"]
)

Running the example multiple times can produce different results because inheritance, blending ratios, mutation, and phenotype expression can use randomness.


Design Philosophy

Allelia separates genetic rules from the meaning of the data.

The library does not need to know that:

(70, 130, 180)

represents an eye color or that:

(120, 80)

represents a point in a procedural character model.

To Allelia, they are simply alleles.

This allows the same inheritance system to be used for character appearance, creature generation, fictional species, procedural traits, or other game and simulation systems.

Applications remain responsible for deciding what their genes represent and how their resulting phenotypes are used.


Version

Current version:

0.1.0

Allelia is currently an early-stage library. The API may change as the library is expanded and tested in additional projects.

Release files for allelia 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for allelia 0.1.0
File Size Uploaded
allelia-0.1.0.tar.gz 9.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for allelia 0.1.0
File Interpreter ABI Platform
allelia-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 16.2 kB

Release files / allelia-0.1.0.tar.gz

Download URL allelia-0.1.0.tar.gz
Size 9.1 kB
Tags Source
SHA-256 checksum
How to use checksums
b7b148370e154aa56ad0b1c7b3a930ab39b56dec903a07eaf1a27fbd6bc4f5d6
BLAKE2b-256 checksum
How to use checksums
6432765507a8e7331516250369d3de672ca0dbe590c7589b1bdfc1481d22df3b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release files / allelia-0.1.0-py3-none-any.whl

Download URL allelia-0.1.0-py3-none-any.whl
Size 7.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4976435c8b24c594c9f5124ceb87f8ed644d6d39af0648601c9ec712af22bcfb
BLAKE2b-256 checksum
How to use checksums
3f31acb27103fed5a596e9ff55c68de2c7f233367580d546af35a40e96b935d4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page