diff --git a/src/js/ibcJS.js b/src/js/ibcJS.js
index c0330d7f151638f8c272aeebe128ec7346bbd020..66a22c0c815ea7790f593dbdbe1eb167748bf204 100644
--- a/src/js/ibcJS.js
+++ b/src/js/ibcJS.js
@@ -1,502 +1,501 @@
 globalThis.ibc = (() => {
-    // These IDs are considered to be unknown parents
-    let or_null = (s) => specificCharacterID(s) ? s : null;
-
-    // Find some sort of state for a slave. Checks first the gene pool, then V.slaves, then the
-    // missing table
-    let find_gp = (id) => (slaveStateById(id) || V.genePool.find((s) => s.ID == id) || ((id in V.missingTable) ? V.missingTable[id] : null) || null);
-
-    // Create a node for the given ID
-    let create_node = (id) => ({
-        id: id, // Node ID
-        mother: null,
-        father: null,
-        nodecodes: [], // NodeCodes
-        nodecodes_str: [], // String version of the NodeCodes, for comparison
-        _coeff: null, // Cached CoI
-        child_count: 0 // Number of children of the node, for computing NodeCodes
-    });
-
-    // Determine the length of the shared prefix between the two NodeCode parameters
-    let prefix_len = (nca, ncb) => {
-        let i = 0;
-        for (i=0; i<Math.min(nca.length, ncb.length); i++) {
-            if (nca[i] !== ncb[i])
-                break;
-        }
-        return i;
-    };
-
-    // Determine the set of longest common prefixes for a node pair
-    let prefixes = (a, b) => {
-        let found = [];
-        let found_s = [];
-        a.nodecodes.forEach(nca => {
-            let match = false;
-            b.nodecodes.forEach(ncb => {
-                let l = prefix_len(nca, ncb);
-                if (l === 0 && match) 
-                    return;
-
-                if (l > 0) {
-                    match = true;
-                    let pfx = nca.slice(0,l);
-                    let pfx_s = pfx.join(';');
-
-                    if (!found_s.includes(pfx_s)) {
-                        found_s.push(pfx_s);
-                        found.push(pfx);
-                    }
-                }
-            });
-        });
-
-        return found;
-    };
-
-    // Search up the tree to find a given NodeCode, starting at `n`
-    let find_nc = (nc, n) => {
-        if (n.nodecodes_str.includes(nc.join(';'))) {
-            return n;
-        }
-
-        let ret = null;
-        if (n.mother !== null)
-            ret = find_nc(nc, n.mother);
-        if (n.father !== null && ret === null)
-            ret = find_nc(nc, n.father);
-
-        return ret;
-    };
-
-    // Determine the set of common ancestors between a node pair
-    let common = (a, b) => {
-        let pfx = prefixes(a, b);
-        let pfx_s = pfx.map(s => s.join(';'));
-        let anc = [];
-
-        while (pfx.length > 0) {
-            let p = pfx.pop(0);
-            pfx_s.pop(0);
-            let ret = find_nc(p, a);
-
-            ret.nodecodes.forEach(nc => {
-                let i = pfx_s.indexOf(nc.join(';'));
-                if (i === -1)
-                    return;
-
-                pfx.pop(i);
-                pfx_s.pop(i);
-            });
-
-            if (anc.findIndex(s => (s[0] == ret)) === -1)
-                anc.push([ret, p]);
-        }
-
-        return anc;
-    };
-
-    // Determine the set of NodeCodes on `n` with prefix `x`
-    let mps = (n, x) => {
-        let x_s = x.join(';');
-        return n.nodecodes.filter(nc => (nc.slice(0, x.length).join(';') === x_s));
-    };
-
-    // Compute the set of all paths to the parents of `n` with prefix `x`
-    let pp = (mother, father, x) => {
-        let m = mps(mother, x);
-        let f = mps(father, x);
-
-        let prod = [];
-        m.forEach(i => {
-            f.forEach(j => {
-                prod.push([i, j]);
-            });
-        });
-
-        return prod;
-    };
-
-    let kinship = (mother, father) => {
-        let _coeff = 0;
-        if (mother === null || father === null)
-            _coeff = 0;
-        else if (mother == father)
-            _coeff = 0.5 * (1 + coeff(mother));
-        else {
-            let cf = 0;
-            let cmn = common(mother, father);
-
-            cmn.forEach(el => {
-                let c = el[0];
-                let p = el[1];
-                let p_s = p.join(';');
-
-                let paths = pp(mother, father, p);
-                let paths_s = paths.map(p => [p[0].join(';'), p[1].join(';')].join(','));
-
-                cmn.forEach(el2 => {
-                    let co = el2[0];
-                    if (co == c)
-                        return;
-                    let found = [];
-                    let m_pp = [];
-                    let f_pp = [];
-
-                    co.nodecodes.forEach(nc => {
-                        if (nc.slice(0, p.length).join(';') != p_s)
-                            return;
-
-                        m_pp = m_pp.concat(mps(mother, nc));
-                        f_pp = f_pp.concat(mps(father, nc));
-                    });
-
-                    m_pp.forEach(mp => {
-                        f_pp.forEach(fp => {
-                            let mf_s = [mp.join(';'), fp.join(';')].join(',');
-                            let i = paths_s.indexOf(mf_s);
-                            if (i === -1)
-                                return;
-                            paths_s.pop(i);
-                            paths.pop(i);
-                        });
-                    });
-                });
-                paths.forEach(p => {
-                    let pfx = prefix_len(p[0], p[1]);
-
-                    cf += 0.5**(p[0].length + p[1].length+1 - 2*pfx) * (1 + coeff(c));
-                });
-            });
-
-            _coeff = cf;
-        }
-
-        return _coeff;
-    };
-
-    // Determine the coefficient of inbreeding of a node `n`
-    let coeff = n => {
-        if (n._coeff === null)
-            n._coeff = kinship(n.mother, n.father);
-        return n._coeff;
-    };
-
-    // Populate the NodeCodes.
-    //
-    // Each node has a set of NodeCodes, which represent the set of paths from it to its ancestors.
-    // NodeCodes here are represented by arrays of integers.
-    //
-    // NodeCodes are constructed recursively in this fashion:
-    //
-    // - Assign each of the founders (nodes with both parents === null) an unique ID, starting from
-    //   0 and incrementing each time (the order doesn't matter); a founder's set of NodeCodes has
-    //   exactly one NodeCode, which is [ID] (an array containing only their ID)
-    //
-    // - For each other node, let M be its child number w.r.t. its mother and N its child number
-    //   w.r.t. its father, i.e. the number of children that the respective parent has had before
-    //   this one (the order is not important to the algorithm, it's arbitrary here for
-    //   convenience). Its set of NodeCodes is the set of all its mother's NodeCodes with M appended
-    //   and all of its father's NodeCodes with N appended. For example, if its mother has the
-    //   NodeCodes [[2]] and M = 3 and its father has the NodeCodes [[0,1], [3,1]] and N = 1 then
-    //   the set of NodeCodes for this node would be
-    //
-    //       [[2, 3], [0, 1, 1], [3, 1, 1]]
-    //
-    // We do this iteratively here, looping over the set of all nodes until each has been assigned
-    // a NodeCode. This requires looping through a number of times equal to the number of
-    // generations, since as soon as both parents have NodeCodes their children's NodeCodes may be
-    // computed.
-    let make_nc = nodes => {
-        // Generate founder NodeCodes
-        let total = Object.keys(nodes).length;
-        let seen = [];
-        let curid = 0;
-        Object.values(nodes).forEach(n => {
-            if (n.mother !== null || n.father !== null)
-                return;
-            n.nodecodes.push([curid]);
-            curid += 1;
-            seen.push(n.id);
-        });
-
-        // Generate the rest of the NodeCodes
-        let oldSeenLength = -1;
-        while (seen.length !== total) {
-            oldSeenLength = seen.length;
-            Object.keys(nodes).forEach(s=> {
-                let n = nodes[s];
-                if (seen.includes(+s)) // We've already done this
-                    return;
-                else if ((n.mother !== null && n.mother.nodecodes.length === 0) || (n.father !== null && n.father.nodecodes.length === 0)) // Too soon, we haven't done its parents
-                    return;
-
-                seen.push(n.id);
-                // Compute the NodeCodes from its parents
-                [n.mother, n.father].forEach((a, i) => {
-                    if (a === null || (n.mother === n.father && i === 1)) // Ignore missing parents/repeated
-                        return;
-
-                    a.nodecodes.forEach(nc => {
-                        // Copy the NodeCode, push the child number, then add it
-                        let nnc = nc.slice();
-                        nnc.push(a.child_count);
-                        n.nodecodes.push(nnc);
-                    });
-                    a.child_count += 1;
-                });
-
-                // NodeCodes must be sorted; this suffices
-                n.nodecodes.sort();
-            });
-            // check to make sure we're progressing...if not, there's a cycle in the graph
-            // dump all the nodes participating in or descended from the cycle and let the player figure it out
-            if (oldSeenLength === seen.length) {
-                const badSlaveIDs = Object.keys(nodes).filter(s => !seen.includes(+s)).map(k => nodes[k].id);
-                throw(`Inbreeding calculation: heritance cycle detected. Check slave IDs: ${badSlaveIDs}`);
-            }
-        }
-
-        // Cache the string NodeCodes
-        Object.values(nodes).forEach(n => {
-            n.nodecodes_str = n.nodecodes.map(nc => nc.join(';'));
-        });
-    };
-
-    // Make nodes for an array of slaves
-    let nodes_slaves = (slaves, ignore_coeffs=false) => {
-        let nodes = {};
-
-        // Recursively create the nodes we need, moving upwards from the given slave
-        let create_node_rec = s => {
-            // Certain parents (e.g. 0, societal elite) are not considered to be related, despite
-            // having the same ID; convert them to null
-            let m = or_null(s.mother);
-            let f = or_null(s.father);
-
-            // Ensure that parent nodes are created
-            [m, f].forEach(p => {
-                if (p !== null && !(p in nodes)) { // Not created, we have to do something
-                    if (p === -1) {
-                        create_node_rec(SugarCube.State.variables.PC);
-                    } else {
-                        // Search for a slave state, genePool entry, or missingTable entry
-                        let gp = find_gp(p);
-                        if (gp !== null) {
-                            // If we find one, we might have ancestry information: recurse
-                            create_node_rec(gp);
-                        } else {
-                            // Otherwise, just create a plain node
-                            nodes[p] = create_node(p);
-                        }
-                    }
-                }
-            });
-
-            if (!(s.ID in nodes)) // Create this node if necessary
-                nodes[s.ID] = create_node(s.ID);
-            // We created its parents earlier, so set them to the actual nodes
-            nodes[s.ID].mother = (m === null) ? m : nodes[m];
-            nodes[s.ID].father = (f === null) ? f : nodes[f];
-
-            // Try to use a cached CoI for performance
-            let sg = find_gp(s.ID);
-            if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
-                nodes[s.ID]._coeff = sg.inbreedingCoeff;
-            }
-        };
-
-        // Populate the nodes
-        slaves.forEach(s=> create_node_rec(s));
-
-        // Populate NodeCodes
-        make_nc(nodes);
-
-        return nodes;
-    }
-
-    // Determine the coefficients of inbreeding of an array of slaves. Returns a mapping of their 
-    // ID to their coefficient of inbreeding
-    let coeff_slaves = (slaves, ignore_coeffs=false) => {
-        let ret = {};
-        // First, pull as many existing CoI off the slaves
-        slaves.forEach(s => {
-            let sg = find_gp(s.ID);
-            if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
-                ret[s.ID] = sg.inbreedingCoeff;
-            }
-        });
-
-        // Now do any we haven't done already
-        slaves = slaves.filter(s => (!(s.ID in ret)));
-        if (slaves.length > 0) {
-            let nodes = nodes_slaves(slaves, ignore_coeffs);
-
-            // Compute coefficients
-            slaves.forEach(s => {
-                ret[s.ID] = coeff(nodes[s.ID]);
-            });
-        }
-
-        return ret;
-    };
-
-    // Determine the kinship between two slaves `a` and `b`
-    let kinship_slaves = (a, b, ignore_coeffs=false) => {
-        if (a === 0 || b === 0)
-            return 0;
-
-        return kinship_one_many(a, [b], ignore_coeffs)[b.ID];
-    };
-
-    // Determine the coefficient of inbreeding of a single slave
-    let coeff_slave = (slave, ignore_coeffs=false) => {
-        if (!ignore_coeffs && "inbreedingCoeff" in slave && slave.inbreedingCoeff !== -1)
-            return slave.inbreedingCoeff;
-
-        let gp = find_gp(slave.ID);
-        if (!ignore_coeffs && gp !== null && "inbreedingCoeff" in gp && gp.inbreedingCoeff !== -1)
-            return gp.inbreedingCoeff;
-
-        return coeff_slaves([slave], ignore_coeffs)[slave.ID];
-    };
-
-    // Determine the kinship between one and many slaves. Returns an mapping from the ID of each of
-    // the slaves in `others` to its kinship with slave `a`
-    let kinship_one_many = (a, others, ignore_coeffs=false) => {
-        if (a === 0) {
-            let ks = {};
-            others.forEach(s => {
-                if (s === 0)
-                    ks[s] = 0;
-                else 
-                    ks[s.ID] = 0;
-            });
-            return ks;
-        }
-
-        let nodes = nodes_slaves(others.concat([a]), ignore_coeffs);
-
-        let ks = {};
-        others.forEach(s => {
-            if (s === -3) {
-                // Fake a slave object for the player's old master
-                s = {ID: -3, mother: 0, father: 0, inbreedingCoeff: 0};
-            }
-
-            if (s === 0)
-                ks[s] = 0;
-            else
-                ks[s.ID] = kinship(nodes[a.ID], nodes[s.ID]);
-        });
-
-        return ks;
-    };
-
-    // Recalculate the inbreeding coefficient for all slaves dependent on the passed IDs (e.g. the
-    // slaves themselves and all of their children). This will replace the inbreeding coefficients
-    // wherever they exist with the computed values, ignoring all cached values.
-    //
-    // This should be called if parents are changed.
-    let recalculate_coeff_ids = (ids) => {
-        // These are all the slave-like objects, i.e. they have ID, mother, and father. There will
-        // be multiple elements with the same ID: we want this, since we have to replace all 
-        // occurrences of the COI for the affected slaves
-        let all_slave_like = V.slaves.concat(V.genePool).concat(V.cribs).concat(V.tanks).concat(Object.values(V.missingTable));
-        if (V.boomerangSlave !== 0)
-            all_slave_like.push(V.boomerangSlave);
-        if (V.traitor !== 0)
-            all_slave_like.push(V.traitor);
-        if (V.activeSlave !== 0)
-            all_slave_like.push(V.activeSlave);
-        all_slave_like.push(V.PC);
-        // Add a fake entry for the PC's old master
-        all_slave_like.push({ID: -3, mother: 0, father: 0, inbreedingCoeff: 0});
-       
-        // Gather the genetics of all current fetuses
-        let all_fetuses = V.slaves.filter(s => s.preg > 0).map(s => s.womb.map(i => i.genetics)).reduce((res, cur) => res.concat(cur), []);
-
-        // Recursively find all of the given ID's children, born and unborn
-        let find_children_rec = (id, cur_slaves, cur_fetuses, cur_fetus_parents) => {
-            // Add fetuses
-            all_fetuses.filter(f => (f.father === id || f.mother === id)).forEach(f => {
-                // We may have to manually add the parents later
-                let actual_f = or_null(f.father);
-                let actual_m = or_null(f.mother);
-                if (actual_f !== null)
-                    cur_fetus_parents.add(actual_f);
-                if (actual_m !== null)
-                    cur_fetus_parents.add(actual_m);
-
-                cur_fetuses.add(f);
-            });
-
-            // Recursively add slaves
-            all_slave_like.filter(s => (s.father === id || s.mother === id)).forEach(s => {
-                if (!cur_slaves.has(s.ID)) {
-                    cur_slaves.add(s.ID);
-                    find_children_rec(s.ID, cur_slaves, cur_fetuses, cur_fetus_parents);
-                }
-            });
-        };
-
-        // We only need slave IDs, since we have to update all of their entries (including GP)
-        let needed_slave_ids = new Set();
-        // Since each fetus has a unique record, a set still suffices
-        let needed_fetuses = new Set();
-        let needed_parent_ids = new Set();
-
-        // Find all the children of the IDs we need to do
-        ids.forEach(id => {
-            needed_slave_ids.add(id);
-            find_children_rec(id, needed_slave_ids, needed_fetuses, needed_parent_ids);
-        });
-
-        // Now we assemble the tree from the slaves
-        let needed_slaves = [];
-        needed_slave_ids.forEach(id => {
-            if (typeof id !== "undefined")
-                needed_slaves.push(all_slave_like.find(s => s.ID === id));
-        });
-        needed_parent_ids.forEach(id => {
-            if (typeof id !== "undefined" && !needed_slave_ids.has(id))
-                needed_slaves.push(all_slave_like.find(s => s.ID == id));
-        });
-        let nodes = nodes_slaves(needed_slaves, true);
-
-        // Now calculate the inbreeding coefficients (they're cached in the tree once calculated)
-        all_slave_like.filter(s => needed_slave_ids.has(s.ID)).forEach(s => {
-            s.inbreedingCoeff = coeff(nodes[s.ID]);
-        });
-
-        // Finally, handle all of the kinship for the fetuses
-        let kinship_cache = new Map(); // Manually cache it
-        needed_fetuses.forEach(f => {
-            if (or_null(f.mother) === null || or_null(f.father) === null) {
-                f.inbreedingCoeff = 0;
-                return;
-            }
-
-            // Use a string of the form "parent;parent" to store the cache value; since kinship is
-            // commutative, the minimum parent ID will be first
-            let kinship_str = Math.min(f.mother, f.father) + ';' + Math.max(f.mother, f.father);
-            if (!kinship_cache.has(kinship_str))
-                kinship_cache.set(kinship_str, kinship(nodes[f.mother], nodes[f.father]));
-
-            f.inbreedingCoeff = kinship_cache.get(kinship_str);
-        });
-    };
-
-    let recalculate_coeff_id = (id) => {
-        return recalculate_coeff_ids([id]);
-    };
-
-    return {
-        coeff: coeff_slave,
-        coeff_slaves,
-        kinship: kinship_slaves,
-        kinship_one_many,
-        recalculate_coeff_ids,
-        recalculate_coeff_id
-    };
+	// These IDs are considered to be unknown parents
+	let or_null = (s) => specificCharacterID(s) ? s : null;
+
+	// Find some sort of state for a slave. Checks first the gene pool, then V.slaves, then the
+	// missing table
+	let find_gp = (id) => (slaveStateById(id) || V.genePool.find((s) => s.ID === id) || ((id in V.missingTable) ? V.missingTable[id] : null) || null);
+
+	// Create a node for the given ID
+	let create_node = (id) => ({
+		id: id, // Node ID
+		mother: null,
+		father: null,
+		nodecodes: [], // NodeCodes
+		nodecodes_str: [], // String version of the NodeCodes, for comparison
+		_coeff: null, // Cached CoI
+		child_count: 0 // Number of children of the node, for computing NodeCodes
+	});
+
+	// Determine the length of the shared prefix between the two NodeCode parameters
+	let prefix_len = (nca, ncb) => {
+		let i = 0;
+		for (i=0; i<Math.min(nca.length, ncb.length); i++) {
+			if (nca[i] !== ncb[i])
+				break;
+		}
+		return i;
+	};
+
+	// Determine the set of longest common prefixes for a node pair
+	let prefixes = (a, b) => {
+		let found = [];
+		let found_s = [];
+		a.nodecodes.forEach(nca => {
+			let match = false;
+			b.nodecodes.forEach(ncb => {
+				let l = prefix_len(nca, ncb);
+				if (l === 0 && match)
+					return;
+
+				if (l > 0) {
+					match = true;
+					let pfx = nca.slice(0, l);
+					let pfx_s = pfx.join(';');
+
+					if (!found_s.includes(pfx_s)) {
+						found_s.push(pfx_s);
+						found.push(pfx);
+					}
+				}
+			});
+		});
+
+		return found;
+	};
+
+	// Search up the tree to find a given NodeCode, starting at `n`
+	let find_nc = (nc, n) => {
+		if (n.nodecodes_str.includes(nc.join(';'))) {
+			return n;
+		}
+
+		let ret = null;
+		if (n.mother !== null)
+			ret = find_nc(nc, n.mother);
+		if (n.father !== null && ret === null)
+			ret = find_nc(nc, n.father);
+
+		return ret;
+	};
+
+	// Determine the set of common ancestors between a node pair
+	let common = (a, b) => {
+		let pfx = prefixes(a, b);
+		let pfx_s = pfx.map(s => s.join(';'));
+		let anc = [];
+
+		while (pfx.length > 0) {
+			let p = pfx.pop(0);
+			pfx_s.pop(0);
+			let ret = find_nc(p, a);
+
+			ret.nodecodes.forEach(nc => {
+				let i = pfx_s.indexOf(nc.join(';'));
+				if (i === -1)
+					return;
+
+				pfx.pop(i);
+				pfx_s.pop(i);
+			});
+
+			if (anc.findIndex(s => (s[0] == ret)) === -1)
+				anc.push([ret, p]);
+		}
+
+		return anc;
+	};
+
+	// Determine the set of NodeCodes on `n` with prefix `x`
+	let mps = (n, x) => {
+		let x_s = x.join(';');
+		return n.nodecodes.filter(nc => (nc.slice(0, x.length).join(';') === x_s));
+	};
+
+	// Compute the set of all paths to the parents of `n` with prefix `x`
+	let pp = (mother, father, x) => {
+		let m = mps(mother, x);
+		let f = mps(father, x);
+
+		let prod = [];
+		m.forEach(i => {
+			f.forEach(j => {
+				prod.push([i, j]);
+			});
+		});
+
+		return prod;
+	};
+
+	let kinship = (mother, father) => {
+		let _coeff = 0;
+		if (mother === null || father === null)
+			_coeff = 0;
+		else if (mother == father)
+			_coeff = 0.5 * (1 + coeff(mother));
+		else {
+			let cf = 0;
+			let cmn = common(mother, father);
+
+			cmn.forEach(el => {
+				let c = el[0];
+				let p = el[1];
+				let p_s = p.join(';');
+
+				let paths = pp(mother, father, p);
+				let paths_s = paths.map(p => [p[0].join(';'), p[1].join(';')].join(','));
+
+				cmn.forEach(el2 => {
+					let co = el2[0];
+					if (co == c)
+						return;
+					let m_pp = [];
+					let f_pp = [];
+
+					co.nodecodes.forEach(nc => {
+						if (nc.slice(0, p.length).join(';') != p_s)
+							return;
+
+						m_pp = m_pp.concat(mps(mother, nc));
+						f_pp = f_pp.concat(mps(father, nc));
+					});
+
+					m_pp.forEach(mp => {
+						f_pp.forEach(fp => {
+							let mf_s = [mp.join(';'), fp.join(';')].join(',');
+							let i = paths_s.indexOf(mf_s);
+							if (i === -1)
+								return;
+							paths_s.pop(i);
+							paths.pop(i);
+						});
+					});
+				});
+				paths.forEach(p => {
+					let pfx = prefix_len(p[0], p[1]);
+
+					cf += 0.5**(p[0].length + p[1].length+1 - 2*pfx) * (1 + coeff(c));
+				});
+			});
+
+			_coeff = cf;
+		}
+
+		return _coeff;
+	};
+
+	// Determine the coefficient of inbreeding of a node `n`
+	let coeff = n => {
+		if (n._coeff === null)
+			n._coeff = kinship(n.mother, n.father);
+		return n._coeff;
+	};
+
+	// Populate the NodeCodes.
+	//
+	// Each node has a set of NodeCodes, which represent the set of paths from it to its ancestors.
+	// NodeCodes here are represented by arrays of integers.
+	//
+	// NodeCodes are constructed recursively in this fashion:
+	//
+	// - Assign each of the founders (nodes with both parents === null) an unique ID, starting from
+	//   0 and incrementing each time (the order doesn't matter); a founder's set of NodeCodes has
+	//   exactly one NodeCode, which is [ID] (an array containing only their ID)
+	//
+	// - For each other node, let M be its child number w.r.t. its mother and N its child number
+	//   w.r.t. its father, i.e. the number of children that the respective parent has had before
+	//   this one (the order is not important to the algorithm, it's arbitrary here for
+	//   convenience). Its set of NodeCodes is the set of all its mother's NodeCodes with M appended
+	//   and all of its father's NodeCodes with N appended. For example, if its mother has the
+	//   NodeCodes [[2]] and M = 3 and its father has the NodeCodes [[0,1], [3,1]] and N = 1 then
+	//   the set of NodeCodes for this node would be
+	//
+	//       [[2, 3], [0, 1, 1], [3, 1, 1]]
+	//
+	// We do this iteratively here, looping over the set of all nodes until each has been assigned
+	// a NodeCode. This requires looping through a number of times equal to the number of
+	// generations, since as soon as both parents have NodeCodes their children's NodeCodes may be
+	// computed.
+	let make_nc = nodes => {
+		// Generate founder NodeCodes
+		let total = Object.keys(nodes).length;
+		let seen = [];
+		let curid = 0;
+		Object.values(nodes).forEach(n => {
+			if (n.mother !== null || n.father !== null)
+				return;
+			n.nodecodes.push([curid]);
+			curid += 1;
+			seen.push(n.id);
+		});
+
+		// Generate the rest of the NodeCodes
+		let oldSeenLength = -1;
+		while (seen.length !== total) {
+			oldSeenLength = seen.length;
+			Object.keys(nodes).forEach(s=> {
+				let n = nodes[s];
+				if (seen.includes(+s)) // We've already done this
+					return;
+				else if ((n.mother !== null && n.mother.nodecodes.length === 0) || (n.father !== null && n.father.nodecodes.length === 0)) // Too soon, we haven't done its parents
+					return;
+
+				seen.push(n.id);
+				// Compute the NodeCodes from its parents
+				[n.mother, n.father].forEach((a, i) => {
+					if (a === null || (n.mother === n.father && i === 1)) // Ignore missing parents/repeated
+						return;
+
+					a.nodecodes.forEach(nc => {
+						// Copy the NodeCode, push the child number, then add it
+						let nnc = nc.slice();
+						nnc.push(a.child_count);
+						n.nodecodes.push(nnc);
+					});
+					a.child_count += 1;
+				});
+
+				// NodeCodes must be sorted; this suffices
+				n.nodecodes.sort();
+			});
+			// check to make sure we're progressing...if not, there's a cycle in the graph
+			// dump all the nodes participating in or descended from the cycle and let the player figure it out
+			if (oldSeenLength === seen.length) {
+				const badSlaveIDs = Object.keys(nodes).filter(s => !seen.includes(+s)).map(k => nodes[k].id);
+				throw(`Inbreeding calculation: heritance cycle detected. Check slave IDs: ${badSlaveIDs}`);
+			}
+		}
+
+		// Cache the string NodeCodes
+		Object.values(nodes).forEach(n => {
+			n.nodecodes_str = n.nodecodes.map(nc => nc.join(';'));
+		});
+	};
+
+	// Make nodes for an array of slaves
+	let nodes_slaves = (slaves, ignore_coeffs=false) => {
+		let nodes = {};
+
+		// Recursively create the nodes we need, moving upwards from the given slave
+		let create_node_rec = s => {
+			// Certain parents (e.g. 0, societal elite) are not considered to be related, despite
+			// having the same ID; convert them to null
+			let m = or_null(s.mother);
+			let f = or_null(s.father);
+
+			// Ensure that parent nodes are created
+			[m, f].forEach(p => {
+				if (p !== null && !(p in nodes)) { // Not created, we have to do something
+					if (p === -1) {
+						create_node_rec(SugarCube.State.variables.PC);
+					} else {
+						// Search for a slave state, genePool entry, or missingTable entry
+						let gp = find_gp(p);
+						if (gp !== null) {
+							// If we find one, we might have ancestry information: recurse
+							create_node_rec(gp);
+						} else {
+							// Otherwise, just create a plain node
+							nodes[p] = create_node(p);
+						}
+					}
+				}
+			});
+
+			if (!(s.ID in nodes)) // Create this node if necessary
+				nodes[s.ID] = create_node(s.ID);
+			// We created its parents earlier, so set them to the actual nodes
+			nodes[s.ID].mother = (m === null) ? m : nodes[m];
+			nodes[s.ID].father = (f === null) ? f : nodes[f];
+
+			// Try to use a cached CoI for performance
+			let sg = find_gp(s.ID);
+			if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
+				nodes[s.ID]._coeff = sg.inbreedingCoeff;
+			}
+		};
+
+		// Populate the nodes
+		slaves.forEach(s=> create_node_rec(s));
+
+		// Populate NodeCodes
+		make_nc(nodes);
+
+		return nodes;
+	};
+
+	// Determine the coefficients of inbreeding of an array of slaves. Returns a mapping of their
+	// ID to their coefficient of inbreeding
+	let coeff_slaves = (slaves, ignore_coeffs=false) => {
+		let ret = {};
+		// First, pull as many existing CoI off the slaves
+		slaves.forEach(s => {
+			let sg = find_gp(s.ID);
+			if (!ignore_coeffs && sg !== null && "inbreedingCoeff" in sg && sg.inbreedingCoeff !== -1) {
+				ret[s.ID] = sg.inbreedingCoeff;
+			}
+		});
+
+		// Now do any we haven't done already
+		slaves = slaves.filter(s => (!(s.ID in ret)));
+		if (slaves.length > 0) {
+			let nodes = nodes_slaves(slaves, ignore_coeffs);
+
+			// Compute coefficients
+			slaves.forEach(s => {
+				ret[s.ID] = coeff(nodes[s.ID]);
+			});
+		}
+
+		return ret;
+	};
+
+	// Determine the kinship between two slaves `a` and `b`
+	let kinship_slaves = (a, b, ignore_coeffs=false) => {
+		if (a === 0 || b === 0)
+			return 0;
+
+		return kinship_one_many(a, [b], ignore_coeffs)[b.ID];
+	};
+
+	// Determine the coefficient of inbreeding of a single slave
+	let coeff_slave = (slave, ignore_coeffs=false) => {
+		if (!ignore_coeffs && "inbreedingCoeff" in slave && slave.inbreedingCoeff !== -1)
+			return slave.inbreedingCoeff;
+
+		let gp = find_gp(slave.ID);
+		if (!ignore_coeffs && gp !== null && "inbreedingCoeff" in gp && gp.inbreedingCoeff !== -1)
+			return gp.inbreedingCoeff;
+
+		return coeff_slaves([slave], ignore_coeffs)[slave.ID];
+	};
+
+	// Determine the kinship between one and many slaves. Returns an mapping from the ID of each of
+	// the slaves in `others` to its kinship with slave `a`
+	let kinship_one_many = (a, others, ignore_coeffs=false) => {
+		if (a === 0) {
+			let ks = {};
+			others.forEach(s => {
+				if (s === 0)
+					ks[s] = 0;
+				else
+					ks[s.ID] = 0;
+			});
+			return ks;
+		}
+
+		let nodes = nodes_slaves(others.concat([a]), ignore_coeffs);
+
+		let ks = {};
+		others.forEach(s => {
+			if (s === -3) {
+				// Fake a slave object for the player's old master
+				s = {ID: -3, mother: 0, father: 0, inbreedingCoeff: 0};
+			}
+
+			if (s === 0)
+				ks[s] = 0;
+			else
+				ks[s.ID] = kinship(nodes[a.ID], nodes[s.ID]);
+		});
+
+		return ks;
+	};
+
+	// Recalculate the inbreeding coefficient for all slaves dependent on the passed IDs (e.g. the
+	// slaves themselves and all of their children). This will replace the inbreeding coefficients
+	// wherever they exist with the computed values, ignoring all cached values.
+	//
+	// This should be called if parents are changed.
+	let recalculate_coeff_ids = (ids) => {
+		// These are all the slave-like objects, i.e. they have ID, mother, and father. There will
+		// be multiple elements with the same ID: we want this, since we have to replace all
+		// occurrences of the COI for the affected slaves
+		let all_slave_like = V.slaves.concat(V.genePool).concat(V.cribs).concat(V.tanks).concat(Object.values(V.missingTable));
+		if (V.boomerangSlave !== 0)
+			all_slave_like.push(V.boomerangSlave);
+		if (V.traitor !== 0)
+			all_slave_like.push(V.traitor);
+		if (V.activeSlave !== 0)
+			all_slave_like.push(V.activeSlave);
+		all_slave_like.push(V.PC);
+		// Add a fake entry for the PC's old master
+		all_slave_like.push({ID: -3, mother: 0, father: 0, inbreedingCoeff: 0});
+
+		// Gather the genetics of all current fetuses
+		let all_fetuses = V.slaves.filter(s => s.preg > 0).map(s => s.womb.map(i => i.genetics)).reduce((res, cur) => res.concat(cur), []);
+
+		// Recursively find all of the given ID's children, born and unborn
+		let find_children_rec = (id, cur_slaves, cur_fetuses, cur_fetus_parents) => {
+			// Add fetuses
+			all_fetuses.filter(f => (f.father === id || f.mother === id)).forEach(f => {
+				// We may have to manually add the parents later
+				let actual_f = or_null(f.father);
+				let actual_m = or_null(f.mother);
+				if (actual_f !== null)
+					cur_fetus_parents.add(actual_f);
+				if (actual_m !== null)
+					cur_fetus_parents.add(actual_m);
+
+				cur_fetuses.add(f);
+			});
+
+			// Recursively add slaves
+			all_slave_like.filter(s => (s.father === id || s.mother === id)).forEach(s => {
+				if (!cur_slaves.has(s.ID)) {
+					cur_slaves.add(s.ID);
+					find_children_rec(s.ID, cur_slaves, cur_fetuses, cur_fetus_parents);
+				}
+			});
+		};
+
+		// We only need slave IDs, since we have to update all of their entries (including GP)
+		let needed_slave_ids = new Set();
+		// Since each fetus has a unique record, a set still suffices
+		let needed_fetuses = new Set();
+		let needed_parent_ids = new Set();
+
+		// Find all the children of the IDs we need to do
+		ids.forEach(id => {
+			needed_slave_ids.add(id);
+			find_children_rec(id, needed_slave_ids, needed_fetuses, needed_parent_ids);
+		});
+
+		// Now we assemble the tree from the slaves
+		let needed_slaves = [];
+		needed_slave_ids.forEach(id => {
+			if (typeof id !== "undefined")
+				needed_slaves.push(all_slave_like.find(s => s.ID === id));
+		});
+		needed_parent_ids.forEach(id => {
+			if (typeof id !== "undefined" && !needed_slave_ids.has(id))
+				needed_slaves.push(all_slave_like.find(s => s.ID === id));
+		});
+		let nodes = nodes_slaves(needed_slaves, true);
+
+		// Now calculate the inbreeding coefficients (they're cached in the tree once calculated)
+		all_slave_like.filter(s => needed_slave_ids.has(s.ID)).forEach(s => {
+			s.inbreedingCoeff = coeff(nodes[s.ID]);
+		});
+
+		// Finally, handle all of the kinship for the fetuses
+		let kinship_cache = new Map(); // Manually cache it
+		needed_fetuses.forEach(f => {
+			if (or_null(f.mother) === null || or_null(f.father) === null) {
+				f.inbreedingCoeff = 0;
+				return;
+			}
+
+			// Use a string of the form "parent;parent" to store the cache value; since kinship is
+			// commutative, the minimum parent ID will be first
+			let kinship_str = Math.min(f.mother, f.father) + ';' + Math.max(f.mother, f.father);
+			if (!kinship_cache.has(kinship_str))
+				kinship_cache.set(kinship_str, kinship(nodes[f.mother], nodes[f.father]));
+
+			f.inbreedingCoeff = kinship_cache.get(kinship_str);
+		});
+	};
+
+	let recalculate_coeff_id = (id) => {
+		return recalculate_coeff_ids([id]);
+	};
+
+	return {
+		coeff: coeff_slave,
+		coeff_slaves,
+		kinship: kinship_slaves,
+		kinship_one_many,
+		recalculate_coeff_ids,
+		recalculate_coeff_id
+	};
 })();