/**
 * This applet demonstrates a simple game. It isn't designed to be general or reusable.
<p>
 * Copyright (C) 2006  <A HREF="http://www.cs.ubc.ca/spider/poole/">David Poole</A>.
<p>
 * This program gives core of the simulation. The GUI is in <A
 * HREF=SGameGUI.java">SGameGUI.java</A>.  The environemnt code is at
 * <A HREF="SGameEnv.java">SGameEnv.java</A>. The
 * function-approximation controller is at <A
 * HREF="SGameFAController.java">SGameFAController.java</A> the
 * features used by that controller are defined at <A
 * HREF="SGameFeatureSet.java">SGameFeatureSet.java</A>.
<p>
 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
<p>
 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.
<p>
 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.


 * @author David Poole  poole@cs.ubc.ca
 * @version 0.4 2007-02-01 */

public class SGameFeatureSet
{
    /** 
	The number of features.
    **/
    public final int NumFeatures=30;

    /**
       Returns the feature values for a state action pair.
     <p>

       The state is made up of:
     <ul>
       <li>  x the x-position of the agent</li>
       <li>  y the y-position of the agent</li>
       <li>  prize the location of the prize, in range 0 to 4, where 4 means no prize</li>
       <li>  dam is true when the agent is damaged</li>
     </p>
     <p>
     The actions defined by <i>act</i> are
     <ul>
     <li> 0 is up</li>
     <li> 1 is right</li>
     <li> 2 is down</li>
     <li> 3 is left</li>
     </ul>
     </p>
    **/

    public double[] featureValues(int x, int y, int prize, boolean dam, int act) 
    {
	double[] result = new double[NumFeatures];

	result[0] = 1;
	result[1] = monsterAhead(x,y,act) ? 1.0 : 0.0;
	result[2] = wallAhead(x,y,act) ? 1.0 : 0.0;
        result[3] = prizeForward(x,y,prize,act) ? 1.0 : 0.0;
	result[4] = dam && repairAhead(x,y,act) ? 1.0 : 0.0;
	result[5] = dam && monsterAhead(x,y,act) ? 1.0 : 0.0;
	result[6] = dam ? 1.0 : 0.0;
	result[7] = !dam ? 1.0 : 0.0;
	result[8] = dam && prizeForward(x,y,prize,act) ? 1.0 : 0.0;
        result[9] = !dam && prizeForward(x,y,prize,act) ? 1.0 : 0.0;

        //	The following defines features 10-29 for different combinations 
        //	of where the prize is and the distances to the edges
	for (int i=0; i<5 ; i++) {
	    result[10+4*i] = prize==i ? x : 0;
	    result[11+4*i] = prize==i ? 4-x : 0;
	    result[12+4*i] = prize==i ? y : 0;
	    result[13+4*i] = prize==i ? 4-y : 0;
	}

	// the following takes into account that to get to prize==0
	// from the top right you first need to go down.	
	// it is a hack!!!  (Think about why we are changing elements 12&13).
	if (prize==0 && x>0 & y<2)
	    {
		result[12] = 4-y;
		result[13] = y;
	    }

	// Here is an alternative that scales to the range [0,1]
	// Does it work as well? Why or why not?
	// for (int i=0; i<5 ; i++) {
	//     result[10+4*i] = prize==i ? nextx(x,act)/4.0 : 0;
	//     result[11+4*i] = prize==i ? 1.0-nextx(x,act)/4.0 : 0;
	//     result[12+4*i] = prize==i ? nexty(y,act)/4.0 : 0;
	//     result[13+4*i] = prize==i ? 1.0-nexty(y,act)/4.0 : 0;
	// }
	// if (prize==0 && nextx(x,act)>0 & nexty(y,act)<2)
	//     {
	// 	result[12] = 1.0-nexty(y,act)/4.0;
	// 	result[13] = nexty(y,act)/4.0;
	//     }
		
	return result;

    }

    /**
       returns the most likely x position after doing action a.
    **/
    private int nextx(int x, int a)
    {
	if (a==1 && x<4)  // right
	    return x+1;
	else if (a==3 && x>0)  // left
	    return x-1;
	else
	    return x;
    }

    /**
       returns the most likely y position after doing action a.
    **/
    private int nexty(int y, int a)
    {
	if (a==2 && y<4)  // down
	    return y+1;
	else if (a==0 && y>0)  // up
	    return y-1;
	else
	    return y;
    }

    /**
       returns true when the square in the direction of the agent is a monster square
    **/
    private boolean monsterAhead(int x, int y, int act)
    {
	if (act%2==0) // action is up or down
	    return monsterAt(x,y+act-1);
	else    // action is left or right
	    return monsterAt(x-act+2,y);
    }

    private boolean monsterAt(int x, int y)
    /**
       returns true when the (x,y) square is a monster square
    **/
    {
	return (x==2 && y==1) ||
	    (x==4 && y==2) ||
	    (x==0 && y==3) ||
	    (x==1 && y==3) ||
	    (x==3 && y==3);
    }

    /**
       returns true if the action heads striaght to a wall
    **/
    private boolean wallAhead(int x, int y, int act)
    {
	return (act==0 && y==0) ||  //up
	    (act==1 && (x==4 || x==0&&y<2 || x==1&&y==0)) || // right
	    (act==2 && y==4) ||  //down
	    (act==3 && (x==0 || x==1 && y<2 || x==2 && y==0));  //left
    }

    /**
       returns true when the action goes towards a prize
    **/
    private boolean prizeForward(int x, int y, int prize, int act)
    {
	return 
	    (act==0 // action is up
	     && (prize==0 //prize is top left
		 && (x==0 || y>2)
		 || prize==1 // prize is top right
		 && ( x==0 && y>2 || x==1 && y>1 || x>1&& y>0)))
	    || 
	    (act==1 // action is right
	     && (prize==1||prize==3)  // prize is on right
	     && (x<4) && !(x==0&&y<2) && !(x==1&&y==0))
	    ||
	    ( act==2  //action is down
	      && ( (prize==2||prize==3)&& y<4 // prize is down
	           || x==0 & y<2 & prize==1
		   || x>0 & y<2 & prize==0))
	    ||
	    (act == 3 // action is left
	     && (prize==0 || prize==2) //prize isleft
	     && x>0 && (y>1 || y==0&& x>2 || y==1 && x>1));
    }

    /** 
	returns true when the agent is in the top-left quadrant and the action 
	takes it towards the repair station. 
     **/
    private boolean repairAhead(int x, int y, int act)
    {
	return x<3 && y<3 && (
	    (act==0 // action is up
	     && ( y>2 // || x>1 && y==2 
		  || x==1 && y>0))
	    ||
	    (act==1 // action is right
	     && x==0 & y>1)
	    ||
	    ( act==2  //action is down
	      && (x==0 && y<2 || x>1 && y==0))
	    ||
	    (act == 3 // action is left
	     && x>1 && !(y==0&&x==2)));
    }
}
