Inheritance Tree: C Programming ( CS50 Project 5)
Inheritance Tree in C
--CS50: Introduction to Computer Science Project 5 Implementation
Project 5:-
The Inheritance problem explores how traits are passed down through generations using concepts from genetics. Instead of working with real DNA, the program models a family tree where each person inherits blood type alleles from their parents.
Problem to Solve (edx)
A person’s blood type is determined by two alleles (i.e., different forms of a gene). The three possible alleles are A, B, and O, of which each person has two (possibly the same, possibly different). Each of a child’s parents randomly passes one of their two blood type alleles to their child. The possible blood type combinations, then, are: OO, OA, OB, AO, AA, AB, BO, BA, and BB.
For example, if one parent has blood type AO and the other parent has blood type BB, then the child’s possible blood types would be AB and OB, depending on which allele is received from each parent. Similarly, if one parent has blood type AO and the other OB, then the child’s possible blood types would be AO, OB, AB, and OO.
🧬 Family Tree Structure (3 Generations)
Child (Generation 0)
alleles: [O][B]
parents[0] → Parent 0
parents[1] → Parent 1
alleles: [O][B]
parents[0] → Parent 0
parents[1] → Parent 1
Parent 0 (Gen 1)
alleles: [O][O]
parents[0] → Grandparent 0
parents[1] → Grandparent 1
alleles: [O][O]
parents[0] → Grandparent 0
parents[1] → Grandparent 1
Parent 1 (Gen 1)
alleles: [B][B]
parents[0] → Grandparent 2
parents[1] → Grandparent 3
alleles: [B][B]
parents[0] → Grandparent 2
parents[1] → Grandparent 3
Grandparent 0
alleles: [A][O]
parents: NULL
alleles: [A][O]
parents: NULL
Grandparent 1
alleles: [O][O]
parents: NULL
alleles: [O][O]
parents: NULL
Grandparent 2
alleles: [B][B]
parents: NULL
alleles: [B][B]
parents: NULL
Grandparent 3
alleles: [B][B]
parents: NULL
alleles: [B][B]
parents: NULL
Project Requirements
- Define struct for each individual
- Create family function which make structure of individual in memory and link to parents
- Random allele generator for grandparents
- Print family tree
- Free family tree
Step 1: Define Struct Person
person (child)
├── parents[0] → person (Parent 0)
│ ├── parents[0] → person (Grandparent)
│ └── parents[1] → person (Grandparent)
└── parents[1] → person (Parent 1)
├── parents[0] → person (Grandparent)
└── parents[1] → person (Grandparent)
In C language we can make struct as user requirement, basically stuct is structure of multiple data types variable.
example.c
// Each person has two parents and two alleles
typedef struct person // declare stuct name here for compiler to identify that parents is an array of pointers to struct person
{
struct person *parents[2]; // pointer to their parent stuct
char alleles[2];
} person;
Step 2: Create family function
The create_family function takes an int (generations) and allocate one person memory for each individual and connected to their two parents, so basically create function return a pointer to person with two parents.
Generation 0 means child and 2 belongs to their grandparents. In create_family function we use recursion to first allocate child memory and then go for their parents and so on.
After base case hit our function put each member allele according to their parents.
Child get their alleles from their parents and grandparents alleles we put by random_allele fn
random.c
// Randomly chooses a blood type allele.
char random_allele()
{
int r = random() % 3;
if (r == 0)
{
return 'A';
}
else if (r == 1)
{
return 'B';
}
else
{
return 'O';
}
}
create_family.c
// Create a new individual with `generations`
person *create_family(int generations)
{
// Allocate memory for new person
person *p = malloc(sizeof(person));
// If there are still generations left to create
if (generations > 1)
{
// Create two new parents for current person by recursively calling create_family
person *parent0 = create_family(generations - 1);
person *parent1 = create_family(generations - 1);
// Set parent pointers for current person
p->parents[0] = parent0;
p->parents[1] = parent1;
// Randomly assign current person's alleles based on the alleles of their parents
p->alleles[0] = parent0->alleles[random()%2];
p->alleles[1] = parent1->alleles[random()%2];
}
// If there are no generations left to create(grandparents)
else
{
// Set parent pointers to NULL
p->parents[0] = NULL;
p->parents[1] = NULL;
// Randomly assign alleles
p->alleles[0] = random_allele();
p->alleles[1] = random_allele();
}
// Return newly created person this pointer is key to access this generation tree
return p;
}
Step 3: Print family
print_family function takes input a pointer which have access to generation tree and a integer means which generations want to print.
First we pass int 0 generation 0 which represent child so first we print present member generation number and their allele then go to each parent(which became a single member like child) and print each parent alleles and so on.
print_family.c
// Print each family member and their alleles.
void print_family(person *p, int generation)
{
// Handle base case
if (p == NULL)
{
return;
}
// Print indentation
for (int i = 0; i < generation * INDENT_LENGTH; i++)
{
printf(" ");
}
// Print person
if (generation == 0)
{
printf("Child (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
else if (generation == 1)
{
printf("Parent (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
else // for grandparents
{
for (int i = 0; i < generation - 2; i++)
{
printf("Great-");
}
printf("Grandparent (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
// Print parents of current generation
print_family(p->parents[0], generation + 1);
print_family(p->parents[1], generation + 1);
}
Step 4: Free family
While freeing memory which allocated by malloc, need to go end of tree and come at root with deleting each struct or node.
free_family.c
void free_family(person *p)
{
// Handle base case
if (p == NULL)
{
return;
}
// Free parents recursively
free_family(p->parents[0]);
free_family(p->parents[1]);
// Free child
free(p);
}
Step 5: Full Code Implementation
inheritance.c
// Simulate genetic inheritance of blood type
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
// Each person has two parents and two alleles
typedef struct person // declare stuct name here for compiler to identify that parents is an array of pointers to struct person
{
struct person *parents[2];
char alleles[2];
} person;
const int GENERATIONS = 3;
const int INDENT_LENGTH = 4;
person *create_family(int generations);
void print_family(person *p, int generation);
void free_family(person *p);
char random_allele();
int main(void)
{
// Seed random number generator
srandom(time(0));
// Create a new family with three generations
person *p = create_family(GENERATIONS);
// Print family tree of blood types
print_family(p, 0);
// Free memory
free_family(p);
}
// Create a new individual with `generations`
person *create_family(int generations)
{
// Allocate memory for new person
person *p = malloc(sizeof(person));
// If there are still generations left to create
if (generations > 1)
{
// Create two new parents for current person by recursively calling create_family
person *parent0 = create_family(generations - 1);
person *parent1 = create_family(generations - 1);
// Set parent pointers for current person
p->parents[0] = parent0;
p->parents[1] = parent1;
// Randomly assign current person's alleles based on the alleles of their parents
p->alleles[0] = parent0->alleles[random()%2];
p->alleles[1] = parent1->alleles[random()%2];
}
// If there are no generations left to create
else
{
// Set parent pointers to NULL
p->parents[0] = NULL;
p->parents[1] = NULL;
// Randomly assign alleles
p->alleles[0] = random_allele();
p->alleles[1] = random_allele();
}
// Return newly created person
return p;
}
// Free `p` and all ancestors of `p`.
void free_family(person *p)
{
// Handle base case
if (p == NULL)
{
return;
}
// Free parents recursively
free_family(p->parents[0]);
free_family(p->parents[1]);
// Free child
free(p);
}
// Print each family member and their alleles.
void print_family(person *p, int generation)
{
// Handle base case
if (p == NULL)
{
return;
}
// Print indentation
for (int i = 0; i < generation * INDENT_LENGTH; i++)
{
printf(" ");
}
// Print person
if (generation == 0)
{
printf("Child (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
else if (generation == 1)
{
printf("Parent (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
else
{
for (int i = 0; i < generation - 2; i++)
{
printf("Great-");
}
printf("Grandparent (Generation %i): blood type %c%c\n", generation, p->alleles[0], p->alleles[1]);
}
// Print parents of current generation
print_family(p->parents[0], generation + 1);
print_family(p->parents[1], generation + 1);
}
// Randomly chooses a blood type allele.
char random_allele()
{
int r = random() % 3;
if (r == 0)
{
return 'A';
}
else if (r == 1)
{
return 'B';
}
else
{
return 'O';
}
}
Example Execution
./inheritance.c
Output :-
Child (Generation 0): blood type OB
Parent (Generation 1): blood type OO
Grandparent (Generation 2): blood type AO
Grandparent (Generatipe BBon 2): blood type OO
Parent (Generation 1): blood type BB
Grandparent (Generation 2): blood type BB
Grandparent (Generation 2): blood type BB
