/*
 * Created on 11.02.2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
 package lukewallwalker;

import de.farafin.snEADy.player.*;
import java.io.Serializable;


/** This is an example  implementation of a snEADy-player.<br>
 * The only thing Luke is able to do is to avoid running into a wall or other snake.
 * If there is no blocked field in front of him, he will go straight ahead. This is just the most basic
 * capability a player should have to survive.<br>
 * <br>
 * Dies ist eine Beispielimplementierung von einem snEADy-player.<br>
 * Das einzige, was Luke kann ist der Wand und anderen Schlangen auszuweichen. Wenn vor ihm kein
 * blockiertes Feld ist, wird er einfach geradeaus fahren. Diese Fähigkeit sollte jeder Spieler haben,
 * der in snEADy wenigstens überleben will.

 *
 * @author roland, lars
 */
public class LukeWallwalker extends Player implements Serializable, PubConstants
{

	/** This string is printed in the player menu.<br>
	 * <br>
	 * Dieser String wird im Playermenü angezeigt.
	 */
	//public static String comment = "May the snake be with you";

	/** The default constructor of Luke. Because snEADy loads the player dynamically, it is necessary to
	 * either define no default constructor at all, or one as public. Constructors with parameter won't
	 * be supported.<br>
	 * <br>
	 * Der standard konstruktor von Luke. Weil snEADy die spieler dydnamisch läd, ist es erforderlich
	 * entweder gar keinen standardkonstruktor anzugeben, oder einen der public ist. Construktoren
	 * mit parametern werden nicht unterstützt.
	 */
	public LukeWallwalker()
	{
		super();
		super.setName("Luke Wallwalker");
	}


	/** To avoid hindrances, Luke needs to see the fields around him. This method returns all three
	 * fields which surround the head of Luke. 3 fields are enough, because a snake can not turn around
	 * 180°.<br>
	 * <br>
	 * Um Hindernissen auszuweichen, müssen die Felder um Luke herum bekannt sein. Diese Methode gibt also
	 * alle drei Felder zurück, die den Kopf von Luke umgeben. 3 Felder reichen aus, weil sich eine Schlange
	 * nicht um 180° drehen kann.
	 *
	 * @param oInfo informations about the own snake.
	 * <br> Informationen über die eigene Schlange.
	 * @param lInfo informations about the level.
	 * <br> Informationen über das Level.
	 *
	 * @return The three fields around the head of the snake.
	 * They are ordered (in the view of the head): left, in front, right.<br>
	 * <br>
	 * Die drei Felder um den Kopf der Schlange herum.
	 * Sie sind wie folgt sortiert (aus der sicht des Kopfes sortiert): links, vorne, rechts.
	 *
	 * @see LevelInfo
	 * @see OwnSnakeInfo
	 * @see PubConstants
	 */
	private char[] getCharAroundHead(OwnSnakeInfo oInfo, LevelInfo lInfo)
	{
		// the variable that should contain the result
		char[] threeFields = new char[3];
		// this variable should contain all fields around the snakes head,
		// even the one its coming from. this fields in here are orientated like the world:
		// fourFields[0] = the field north of head,
		// fourFields[1] = the field east of head,
		// fourFields[2] = the field south of head,
		// fourFields[3] = the field west of head,
		char[] fourFields = new char[4];

		// height and width of the playfield, I'm just too lazy to write every time "lInfo.blabla"..
		// and too, its more readable if it is short.
		int height = lInfo.height, width = lInfo.width;
		// same, with playfield again..
		// something very important about the playfield: It is organized like a matrix. So upper
		// left coordinats are 0-0, the first index represents the line, the second the row.
		// Its the same like in a matrix. So the playfield is addressed with playField[line][row].
		// see for this LevelInfos.playField.
		char[][] plField = lInfo.playField;
		// and the head position of the snake
		int l = oInfo.headPosLine, r = oInfo.headPosRow;



		// fourFields[0] = the field north of head, so the y - value must be one less than before.
		// we need the modulo because if we are in line 0, the line above is the last line in the field.
		// the same is for all other fields we calculate. we don't want to ask every time if we are at one
		// margin of the playfield, so we use mod.
		fourFields[0] = plField[mod((l-1), height)][r]; // one field north of the head
		fourFields[1] = plField[l][mod((r+1), width)];  // one field east of the head
		fourFields[2] = plField[mod((l+1), height)][r]; // one field south of the head
		fourFields[3] = plField[l][mod((r-1), width)];  // one field west of the head

		// now we have the fields around the snakes head. now we need to find out
		// which three of them we need.
		// so we have 4 different cases, one for each direction. And we need to take care of the
		// direction of the players head and the order we gave our threeField variable.
		// we start always with the value left of the head, put this in as 0-element.
		// the field directly before the head is the 1-element,
		// and the field right of the head is the 2. element.
		switch(oInfo.headDirection)
		{
			case NORTH: // the snakes head watches north, so we need west, north and east.
				// west of head is left of head
				threeFields[0] = fourFields[3];
				// north of head is directly before the head
				threeFields[1] = fourFields[0];
				// and right of head is east of head.
				threeFields[2] = fourFields[1];
				break;
			case EAST: // the snakes head watches east, so we need north, east and south
				threeFields[0] = fourFields[0];
				threeFields[1] = fourFields[1];
				threeFields[2] = fourFields[2];
				break;
			case SOUTH: // the snakes head watches south, so we need east, south and west.
				threeFields[0] = fourFields[1];
				threeFields[1] = fourFields[2];
				threeFields[2] = fourFields[3];
				break;
			case WEST: // the snakes head watches west, so we need south, west and north.
				threeFields[0] = fourFields[2];
				threeFields[1] = fourFields[3];
				threeFields[2] = fourFields[0];
				break;
			default:;
		}

		return threeFields;
	}


	/** <b>The modulo operator n mod m:</b><br>
	 * <br>
	 * For calculate the indices  of the playfield, we need indices which can wrap around.
	 * For Example: Is m = 10: than is ( 12 mod 10 ) = 2, ( 26 mod 10 ) = 6 ..  and so on. But
	 * We need the same effect in the negative direction: ( -26 mod 10 ) = 4. Why 4? Think of
	 * a circle which is divided  into 10 intervals (0 to 9). If you go around the positive direction,
	 * (clockwise), than you are in interval number 6 if go 26 times clockwise. So you
	 * go two times full around and 6 steps more. But if like to go counterclockwise, than it is
	 * the slightly different. If you are in interval 0 (this is conform to interval 10, because 10 mod 10 = 0)
	 * and go one counterclockwise (the analogy would be -1 mod 10), than you are in interval 9.
	 * The same is if you go 26 times counterclockwise. Than you go two times full around and 6 steps more left,
	 * so you end up in interval -6 = 4. Thats why -26 mod 10 = 4.
	 * But the problem is, the Java-mod operator '%' does work different.
	 * It calculates (-26%10) = -6. Thats why we need to write our own modulo function which works
	 * correct even with negative n, and calculates the correct positive value: ((n%m) + m).<br>
	 * <br>
	 * <b>Der modulo  operator n mod m:</b><br>
	 * <br>
	 * Um die Indizes des Spielfeldes zu berechnen, brauchen wir Indizes, die im Kreis laufen zu lassen.
	 * Beispielsweise: Ist m = 10, dann ist ( 12 mod 10 ) = 2, ( 26 mod 10 ) = 6 .. und so weiter. Aber
	 * Wir brauchen den gleichen Effekt auch in die negative Richtung: -26 mod 10 = 4.
	 * Warum 4? Stell dir einen Kreis vor, der in 10 Abschnitte unterteilt ist (0 bis 9). Wenn in positive
	 * Richtung (also im Urzeigersinn) den Kreis entlang wandert, gelangst du zu
	 * Abschnitt 6 wenn du 26 mal nach im Urzeigersinn gehst. Das sind dann also zwei Umrundungen und dann noch
	 * 6 Schritte weiter. Aber wenn du gegen den Uhrzeigersinn herum gehen möchtest, dann ist es ein wenig anders.
	 * Wenn du in 0 startest (entspricht Abschnitt 10, denn 10 mod 10 = 0) und einen Schritt gegen den
	 * Uhrzeigersinn gehst (analog wäre -1 mod 10), dann gelangst du zu Intervall 9. Das selbe passiert
	 * wenn du 26 mal gegen den Uhrzeigersinn gehst, dann wanderst du 2 mal komplett herum,
	 * dann noch 6 Schritte weiter und landest in Feld -6 = 4. Deswegen ist -26 mod 10 = 4.
	 * Aber das Problem ist, dass der Java - mod operator '%' anders arbeitet.
	 * Dieser berechnet (-26%10) = -6. Darum müssen wir unsere eigene modulo-Funktion schreiben, der
	 * auch für negative n den richtigen, positiven Wert aus spuckt: ((n%m) + m).
	 *
	 * @param n the left operant of mod<br>
	 * der linke mod-operant
	 * @param m the right operant of mod<br>
	 * der rechte mod-operant
	 * @return n mod m (= "n%m", if it would work)<br>
	 * n mod m ("n%m", wenn es funktionieren würde)
	 */
	private int mod(int n, int m)
	{
		// mod is only defined for values bigger than 0.
		if(m <= 0) return 0;

		// if n is not negative, we know that % does calculate correct.
		if(n >= 0) return n%m;
		// if n is negative, we know that n%m is negative, but |n%m| < m.
		// so that means n%m + m > 0 and has the value we are looking for.
		return ((n%m) + m);
	}

	/** This method controls if a field is blocked or not. There are three cases:
	 * The field is a wall, an other snake or an exit field if the exit is still closed.
	 * The exit is closed if exitTime is bigger than the current gameTime. The exitTime
	 * is the time the exit is opened. <br>
	 * <br>
	 * Diese Methode kontrolliert ob ein Feld blockiert ist oder nicht. Es gibt drei Fälle:
	 * Das Feld ist eine Wand, eine andere Schlange oder ein Exit-Feld solange der Exit noch
	 * geschlossen ist. Der Exit ist genau dann geschossen, wenn die exitTime größer ist als
	 * die aktuelle Spielzeit: gameTime. exitTime ist praktisch die zeit zu der der Exit auf
	 * gemacht wird.
	 *
	 * @param c the char-value of the playfield<br>
	 * der cahr-wert des feldes
	 * @param gInfo information about the game<br>
	 * Inromationen über das Spiel.
	 * @return Returns true if the <code>c</code> is a blocked char, false otherwise.
	 *
	 * @see GameInfo
	 * @see PubConstants
	 */
	private boolean isBlocked(char c, GameInfo gInfo)
	{
		// if c is a wall, return true
		if(c == WALL) return true;
		// if c is one of the snakes, return true (see the value of the constants)
		if(PLAYER_0 <= c && c <= PLAYER_9)  return true;
		// if c is exit and the exit is closed, return true
		if(c == EXIT && gInfo.gameTime < gInfo.exitTime)  return true;
		// in all other cases return false.
		return false;
	}

	/* (non-Javadoc)
	 * @see de.farafin.snEADy.player.C_Player#calculate(de.farafin.snEADy.player.D_PlayerInfo)
	 */
	/** This is now the method you should implement. The only thing that needs to be decided is in which
	 * direction the snake should go. This is the method you should implement for your own player,
	 * how complex you do this, is your decision :) LukeWallwalker is happily very simple.<br>
	 * <br>
	 * Dies hier ist nun die Methode die ihr implementieren sollt. Das einzige was entschieden werden muss
	 * ist die Richtung in die sich die Schlange bewegen soll. Wie komplex ihr das macht,
	 * ist eure Entscheidung. :) LukeWallwalker ist zum Glück sehr einfach.
	 *
	 * @param gInfo informations about the game<br>
	 * Informationen über das spiel
	 * @param lInfo informations about the level(playfield)<br>
	 * Informationen über das Level (spielfeld)
	 * @param sInfo informations about all other snakes<br>
	 * Informationen über alle anderen schlangen
	 * @param oInfo informations about the own snake<br>
	 * Informationen über die eigene schlange
	 *
	 * @see GameInfo
	 * @see LevelInfo
	 * @see SnakeInfo
	 * @see OwnSnakeInfo
	 * @see PubConstants
	 */
	public void calculate(GameInfo gInfo, LevelInfo lInfo, SnakeInfo []sInfo, OwnSnakeInfo oInfo)
	{
		// the three char fields surrounding the head of the snake. see above for more information's.
		char[] threeFields = null;

		// if the snake doesn't move in this gameCycle, there is no need to decide anything. So
		// the method just stops here. If you like to calculate something in this time,
		// its free for you to do so. Luke doesn't need to.
		if(gInfo.gameTime < oInfo.nextMoveTime) return;



		// calculate the three fields.
		threeFields = this.getCharAroundHead(oInfo, lInfo);

		// if the field in front of the snake is not blocked, the snake can move straight ahead.
		if(!isBlocked(threeFields[1], gInfo))
		{
			this.setTurnDirection(TURN_NONE);
			return;
		}
		// if the field left of the snake is not blocked, the snake can turn left.
		if(!isBlocked(threeFields[0], gInfo))
		{
			this.setTurnDirection(TURN_LEFT);
			return;
		}
		// if the field right of the snake is not blocked, the snake can turn right.
		if(!isBlocked(threeFields[2], gInfo))
		{
			this.setTurnDirection(TURN_RIGHT);
			return;
		}

		// if nothing is free, luke cant do anything.. so he just runns straight into his death..
		this.setTurnDirection(TURN_NONE);


		// Lukes last words.. well.. stolen by Obiwan but they are not excectly the same ;-)
		if(oInfo.snakeLength <= 2) say(oInfo, "You can't win! If you kill me now, I shall become more powerful than you could possibly imagine!!");
	}
}
