// gpjpp example program
// Copyright (c) 1997, Kim Kokkonen
//
// This program is free software; you can redistribute it and/or 
// modify it under the terms of version 2 of the GNU General Public 
// License as published by the Free Software Foundation.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
// Send comments, suggestions, problems to kimk@turbopower.com

import java.io.*;
import gpjpp.*;

//extend GPGene to evaluate equations

class V2_GPGene extends GPGene {
    static final boolean DBG = false;

    //public null constructor required during stream loading only

    //this constructor called when new genes are created
    V2_GPGene(GPNode gpo) { super(gpo); }

    //this constructor called when genes are cloned during reproduction
    V2_GPGene(V2_GPGene gpo) { super(gpo); }

    //called when genes are cloned during reproduction
    protected Object clone() { return new V2_GPGene(this); }

    //ID routine isA() required only for streams

    //must override GPGene.createChild to create AntGene instances
    public GPGene createChild(GPNode gpo) { return new V2_GPGene(gpo); }

    //protected divide, here protected by IEEE floating point standards
    double divide(double x, double y) {
        //return NaN or INF if y = 0
      if (y==0) {
	return x;
      }
      else return x/y;
    }


    //called by one of othello players (i.e. OthelloGPTester) 
    //for each branch of each GP
    //evaluate works with or without ADFs 
    double evaluate(V2_Evalparams params, V2_GP gp) { 
      double ret = 0.0; //avoid compiler error

        if (isFunction()) {
            switch (node.value()) {
                case '*':
                    ret = ((V2_GPGene)get(0)).evaluate(params, gp) *
                          ((V2_GPGene)get(1)).evaluate(params, gp);
		    if(DBG)
			System.out.println(" * : " + ret);
                    break;

                case '+':
                    ret = ((V2_GPGene)get(0)).evaluate(params, gp) +
                          ((V2_GPGene)get(1)).evaluate(params, gp);
		    if(DBG)
			System.out.println(" + : " + ret);
                    break;

                case '-':
                    ret = ((V2_GPGene)get(0)).evaluate(params, gp) -
                          ((V2_GPGene)get(1)).evaluate(params, gp);
		    if(DBG)
			System.out.println(" - : " + ret);
                    break;

                case '%':
                    //use protected divide function
		    ret = divide(((V2_GPGene)get(0)).evaluate(params, gp),
                          ((V2_GPGene)get(1)).evaluate(params, gp));
		    if(DBG)
			System.out.println(" divide : " + ret);
                    break;

                case 'A':
                    //ADF0 function call.  We have access to that
                    //  subtree, as the GP gave us a reference to itself as
                    //  parameter.  We first evaluate the subtrees, and then
                    //  call the adf with the parameters
                    params.arg0 = ((V2_GPGene)get(0)).evaluate(params, gp);
		    params.arg1 = ((V2_GPGene)get(1)).evaluate(params, gp);

                    ret = ((V2_GPGene)gp.get(1)).evaluate(params, gp);

                    break;

                default:
                    throw new RuntimeException("Undefined function type "+node.value());
            }

        } else { //is terminal
            switch (node.value()) {
                case 'm':
                    ret = params.me_change;
		    if(DBG)
			System.out.println(" me_change : " + ret);
                    break;
	        case 'o':
		    ret = params.opponent_change;
		    if(DBG)
			System.out.println(" opponent_change : " + ret);
		    break;

                case 'C':
                    ret = params.me_corners_gain;
		    if(DBG)
			System.out.println(" me_corners_gain : " + ret);
                    break;

                case 'c':
                    ret = params.opponent_corners_gain;
		    if(DBG)
			System.out.println(" opponent_corners_gain : " + ret);
                    break;
	    case 'x':
		ret = params.col_from_center;
		if(DBG)
		    System.out.println(" col_from_center : " + ret);
		break;
	    case 'y':
		ret = params.row_from_center;
		if(DBG)
		    System.out.println(" row_from_center : " + ret);
		break;
	    case 'N':
		ret = params.me_near_corners;
		if(DBG)
		    System.out.println(" me_near_corners : " + ret);
		break;
	    case 'n':
		ret = params.opponent_near_corners;
		if(DBG)
		    System.out.println(" opponent_near_corners : " + ret);
		break;
	    case 'E':
		ret = params.me_edges;
		if(DBG)
		    System.out.println(" me_edges : " + ret);
		break;		
	    case 'e':
		ret = params.opponent_edges;
		if(DBG)
		    System.out.println(" opponent_edges : " + ret);
		break;		
	    case 0:
		ret = 0;
		if(DBG)
		    System.out.println(" 0 ");
		break;
	    case 5:
		ret = 5;
		if(DBG)
		    System.out.println(" 5 ");
		break;
	    case 10:
		ret = 10;
		if(DBG)
		    System.out.println(" 10 ");
		break;
	    case 1:
		ret = params.arg0;
		break;
	    case 2:
		ret = params.arg1;
		break;
		
	    default:
		throw new RuntimeException("Undefined terminal type "+node.value());
            }
        }

        //keep results in reasonable range (needed only when NaN not used)
        //if (ret > 1.0e30)
        //    ret = 1.0e30;
        //else if (ret < -1.0e30)
        //    ret = -1.0e30;

        return ret;
    }
}



