//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //============================================================================= package org.uncommons.watchmaker.examples.biomorphs; import java.util.Arrays; /** * <p>Candidate representation for a biomorph. We could just as easily have * used an array of integers or a bit string representation with Gray codes * but for clarity we will use this more object-oriented representation that * conveniently combines state and related logic.</p> * * <p>The Biomporph class encapsulates 9 genes as described by Dawkins in his * "The Evolution of Evolvability" paper.</p> * * @author Daniel Dyer */ public final class Biomorph { /** The total number of genes that make up a biomorph. */ public static final int GENE_COUNT = 9; /** The minimum permitted value for most genes. */ public static final int GENE_MIN = -5; /** The maximum permitted value for most genes. */ public static final int GENE_MAX = 5; /** The index of the gene that controls biomporph size. */ public static final int LENGTH_GENE_INDEX = 8; /** The minimum permitted value for the length gene. */ public static final int LENGTH_GENE_MIN = 1; /** The maximum permitted value for the length gene. */ public static final int LENGTH_GENE_MAX = 7; private final int[] genes; private int[][] phenotype; /** * Creates a biomorph with the specified genes. * @param genes A 9-element array. The final element must be a positive * value. */ public Biomorph(int[] genes) { if (genes.length != GENE_COUNT) { throw new IllegalArgumentException("Biomorph must have " + GENE_COUNT + " genes."); } this.genes = genes.clone(); } /** * Returns an array of integers that represent the graphical pattern * determined by the biomorph's genes. * @return A 2-dimensional array containing the 8-element dx and dy * arrays required to draw the biomorph. */ public int[][] getPatternPhenotype() { if (phenotype == null) { // Decode the genes as per Dawkins' rules. int[] dx = new int[GENE_COUNT - 1]; dx[3] = genes[0]; dx[4] = genes[1]; dx[5] = genes[2]; dx[1] = -dx[3]; dx[0] = -dx[4]; dx[7] = -dx[5]; dx[2] = 0; dx[6] = 0; int[] dy = new int[GENE_COUNT - 1]; dy[2] = genes[3]; dy[3] = genes[4]; dy[4] = genes[5]; dy[5] = genes[6]; dy[6] = genes[7]; dy[0] = dy[4]; dy[1] = dy[3]; dy[7] = dy[5]; phenotype = new int[][]{dx, dy}; } return phenotype; } /** * @return The value of the gene that controls the size of this biomorph. */ public int getLengthPhenotype() { return genes[LENGTH_GENE_INDEX]; } /** * Returns the 9 genes that make up the biomorph's genotype. * @return A 9-element array containing this biomorph's genes. */ public int[] getGenotype() { return genes.clone(); } /** * Compares the genes of two Biomporph objects and returns true if they are * identical. * @param obj The object to compare with this one. * @return True if the argument is a Biomoprh instance and the 2 * biomorphs have the same genes, false otherwise. */ @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null || getClass() != obj.getClass()) { return false; } Biomorph biomorph = (Biomorph) obj; return Arrays.equals(genes, biomorph.genes); } /** * Over-ridden to be consistent with {@link #equals(Object)}. Biomorphs * with identical genes return identical hash codes. * @return This object's hash code. */ @Override public int hashCode() { return Arrays.hashCode(genes); } }