Source: genetic/individual.js

var utils = require('./../infrastructure/utils');
var clone = require('clone');

/**
 * Genetic algorithm individual
 * @constructor
 * @param {object} options - Individual options
 * @param {number} options.minLength - The minimum number of genes
 * @param {number} options.maxLength - The maximum number of genes
 * @param {function} options.geneFactory - A function that returns a random gene
 * @property {object[]} body - An array of genes that represent a single chromosome
 * @property {number} fitness - The individuals fitness rating
 * @property {object} options - Individual options
 */
var Individual = function (options) {
    this.body = null;
    this.fitness = null;
    this.options = options;
    this.validateRequiredOptions();
    this.initialise();
    return this;
};

/**
 * Validates the individuals current options
 * @throws An exception will occur if a required option is missing
 * @returns {Individual} Reference to current object for chaining
 */
Individual.prototype.validateRequiredOptions = function () {
    if (!this.options) {
        throw "options are required";
    } else if (this.options.minLength === undefined) {
        throw "option 'minLength' is required";
    } else if (this.options.maxLength === undefined) {
        throw "option 'maxLength' is required";
    } else if (!this.options.geneFactory) {
        throw "option 'geneFactory' is required";
    }
    return this;
};

/**
 * Re creates the individuals body with randomly generated genes
 * @returns {Individual} Reference to current object for chaining
 */
Individual.prototype.initialise = function () {
    var length = utils.randBetween(this.options.minLength, this.options.maxLength + 1);
    this.body = [];
    for (var i = 0; i < length; i++) {
        this.body.push(this.options.geneFactory(this));
    }
    return this;
};

/**
 * Creates a deep copy of the individual
 * @returns {Individual} A copy of the Individual instance
 */
Individual.prototype.copy = function () {
    return clone(this);
};

/**
 * Creates a deep copy of the individual and then re initialises
 * @returns {Individual} A new individual based on the current individual
 */
Individual.prototype.createNew = function () {
    var newIndividual = this.copy();
    newIndividual.initialise();
    return newIndividual;
};

/**
 * Mutates the individual by swapping a single gene with a randomly created gene
 * @returns {Individual} Reference to current object for chaining
 */
Individual.prototype.mutate = function () {
    this.body[utils.randBetween(0, this.body.length)] = this.options.geneFactory(this);
    this.fitness = null;
    return this;
};

/**
 * Determine whether the individual body length can change (i.e. minLength === maxLength)
 * @returns {boolean} A true value if the indiviuals body is of fixed length, otherwise false
 */
Individual.prototype.isFixedLength = function () {
    return this.options.minLength === this.options.maxLength;
};

exports.Individual = Individual;