//******************************************************************************
// HexagonalTotalistic.java
//******************************************************************************
package springlets;

import net.jmge.prj.csprings.Springlet;

//==============================================================================
// Extends Cellsprings with an interesting rulespace while providing an
// example/template springlet definition as part of the "springlets API"
// tutorial.  (See the Springlet Programming section of the Cellsprings
// documentation.)
//
// John Elliott, August 2000
//==============================================================================
public class HexagonalTotalistic extends Springlet {

  // runtime parameter names and default values (specifies 'YChromosome' rule)
  private static final String[][] FIELDS = { 
    {"BCounts", "2"}, 
    {"SCounts", "2"}
  };  

  // quick lookup to track the ASCII rule definition 
  private int[] ruleTable = new int[2];  
    
  //----------------------------------------------------------------------------
  // We must define a default constructor that passes information to the
  // superclass via its only constructor.
  //----------------------------------------------------------------------------
  public HexagonalTotalistic()
  {
    super(
      3,      // pass the default cellsize to the superclass
      true,   // inform the system that we want to parameterize cellsize
      FIELDS  // our own runtime parameter definitions
    );

    // initialize numeric rule table from the default ASCII parameters
    for (int i = 0; i < FIELDS.length; ++i)
      parseParameter(FIELDS[i][0], FIELDS[i][1]);
  }    
 
  //----------------------------------------------------------------------------
  // We override this superclass method to provide a description of our
  // rulespace to the user.
  //----------------------------------------------------------------------------
  public String[] getDescription()
  {
    return new String[] {
      "DEFINED SPACE - Radius-1 hexagonal hood, outer totalistic, with and",
      "without decay.  (This hood is accomplished on our rectangular lattice",
      "as the von Neumann plus NW and SE.)",
      "PROGRAMMER - John Elliott, August 2000",
      "COMMENTS - Ben Schaeffer's fascinating 'YChromosome' rule inspired this",
      "generalization, which conveniently extends Cellsprings while providing",
      "a good springlet programming example.  Although springlet performance",
      "can't match the built-in totalistic rules (which utilize a less generic",
      "state abstraction), the plug-in has already proven its usefulness by",
      "facilitating the discovery of several interesting rules."
    };
  }  

  //----------------------------------------------------------------------------
  // We override this method because we defined runtime parameters other than
  // cellsize at construction.  (Cellsize updates are handled by the
  // superclass.)  The method is called when the user enters a new parameter
  // value; it's our responsibility to track the changes with our own instance
  // variables ('ruleTable' in the present class).
  //----------------------------------------------------------------------------
  protected boolean parameterChanged(String name, String value) 
  {
    return parseParameter(name, value);     
  }

  //----------------------------------------------------------------------------
  // This override is where the action is, i.e., where we implement our rule's
  // algorithm.  Among other things, this example shows how to use the inherited
  // neighbor-index arrays.
  //----------------------------------------------------------------------------
  protected int computeCell(int i, int j)
  {
    // copy current center cell value for handier (and faster) access
    int curval = mCells[i][j];

    // if the cell is in a decay cycle, proceed with it
    if (curval > 1)
      return (curval + 1) % cellSize; // early RETURN - cycrement of current val

    // count the number of 1-valued neighbors 
    int count = 0;
    if (mCells[iE[i]][jS[j]] == 1) ++count;  
    if (mCells[iW[i]][jN[j]] == 1) ++count;
    if (mCells[iE[i]][   j ] == 1) ++count;
    if (mCells[iW[i]][   j ] == 1) ++count;
    if (mCells[   i ][jS[j]] == 1) ++count;
    if (mCells[   i ][jN[j]] == 1) ++count;

    // look up the OT rule's output for our present neighbor-count and center
    // value (the latter is either 0 or 1 if we've got this far)
    int entry = 1 & (ruleTable[curval] >> count);

    // if we're a decay rule and "death" is called for, start the decay cycle,
    // otherwise just return the OT table entry
    return cellSize > 2 && curval == 1 && entry == 0 ? 2 : entry;
  }

  //----------------------------------------------------------------------------
  // We need to override this because 'ruleTable' is mutable.
  //----------------------------------------------------------------------------
  protected Object clone()
  {    
    HexagonalTotalistic copy = (HexagonalTotalistic) super.clone();
    copy.ruleTable = (int[]) ruleTable.clone();    
    return copy;
  }    

  //----------------------------------------------------------------------------
  // Besides constructors and overrides, we can define any methods of our own
  // we care to.  In this case, we're just doing a routine for parsing the
  // ASCII parameters.  It also provides an example of how you might go about
  // input validation should you want to provide a polished springlet for use
  // by others.
  //----------------------------------------------------------------------------  
  private boolean parseParameter(String name, String value) 
  {
    // check the passed name against the birth and survival field names;
    // notice the assumed correspondence between field index and
    // the value of the center cell as input to the lookup (i.e., the birth
    // and survival parameters are the first two, and in that order)
    for (int ifield = 0; ifield < ruleTable.length; ++ifield)
      if (name.equals(FIELDS[ifield][0]))
      {
        // we pack the output bits, where neighbor-count corresponds 
        // directly to bit significance
        int bits = 0;
        for (int i = 0; i < value.length(); ++i)
        {
          // notice radix of 7 for validation (char must be digit 0-6)
          int naborcount = Character.digit(value.charAt(i), 7);
          if (naborcount == -1)    // if invalid digit
            return false;          // early RETURN - halt parse and say so
          bits |= 1 << naborcount; // otherwise OR bit into 'count' position
        }  
        ruleTable[ifield] = bits; 
      }
      
    return true;  
  }
}
