#include "Player.hh"

using namespace std;


/**
 * Write the name of your player and save this file
 * with the same name and .cc extension.
 */
#define PLAYER_NAME Smart

const Dir DIRS[] = {Top, Bottom, Left, Right};



struct PLAYER_NAME : public Player {


	  /**
	   * Factory: returns a new instance of this class.
	   * Do not modify this function.
	   */
	  static Player* factory () {
		return new PLAYER_NAME;
	  }


	  /**
	   * Attributes for your player can be defined here.
	   */
	  vector<double> dirWeight;

	  /**
	   * Play method.
	   *
	   * This method will be invoked once per each round.
	   * You have to read the board here to place your actions
	   * for this round.
	   *
	   */
	  virtual void play () {
		// initialization

		const Poquemon& p = poquemon(me());

		if (p.alive) {
			// Searching a possible rival.
			Dir objective = findObjective(p);
			if (objective != None) attack(objective);
			// moving Poquemon to best direction.
			else {
				Dir d = getDirection();
				move(d);
			}
		}

	}

	Dir findObjective(const Poquemon& p) {
		vector <int> dir = {search(p, Top), search(p, Bottom), search(p, Left), search(p, Right)};
		int closest = 0;
		int size = dir.size();
		for (int i = 1; i < size; ++i)
				if (dir[i] < dir[closest]) closest = i;
		if (dir[closest] <= p.scope) return DIRS[closest];
		return None;
	}

	int search(const Poquemon& p, Dir d) {
		int dist = 1;
		Pos pIni = p.pos;
		while (dist <= p.scope) {
			pIni = dest(pIni, d);
			if (cell_type(pIni) == Wall) return p.scope+1;
			int idCell = cell_id(pIni);
			if ( idCell != -1) {
				Poquemon p1 = poquemon(idCell);
				if (p1.defense <= p.attack) return dist;
			}
			++dist;
		}
		return p.scope+1;
	}

	Dir getDirection() {
		Pos myPosition = poquemon(me()).pos;
		dirWeight = vector<double>(4,0.0);
		queue<Pos> movs;
		movs.push(myPosition);
		vector < vector<bool> > seenCell(rows(), vector<bool>(cols(), false));
		seenCell[myPosition.i][myPosition.j] = true;
		map<Pos,pair<Pos,Dir> > M;
		M[myPosition] = pair<Pos,Dir>(myPosition,None);
		while (not movs.empty()) {
			Pos currentPosition = movs.front();
			for(int i = 0; i < 4; ++i) {
				Pos nextPos = dest(currentPosition, DIRS[i]);
				if(poquemon_can_move(nextPos, seenCell)) {
					movs.push(nextPos);
					seenCell[nextPos.i][nextPos.j] = true;
					M[nextPos] = pair<Pos,Dir>(currentPosition, DIRS[i]);
					analizeCurrentPosition(currentPosition, M);
				}
			}
			movs.pop();
		}
		int max = 0;
		for (int i = 1; i < 4; ++i) {
			if (dirWeight[max] < dirWeight[i]) max = i;
		}
		return DIRS[max];
	}


	// Function to verify if 'a' is not a Wall and it is not seen yet.
	bool poquemon_can_move(const Pos a, vector< vector<bool> >& seenCell) {
		if (seenCell[a.i][a.j]) return false;
		if (cell_type(a) == Wall) return false;
		return true;
	}

	/** Function to get a ponderation if some bonus stores there. We will go back in the
	 * route done to know the first direction token
	 */
	void analizeCurrentPosition(const Pos currentPosition, map<Pos, pair<Pos,Dir> >& M) {
		CType ct = cell_type(currentPosition);
		int distance = 1;
		int initialDirection = dir2num(getInitialDirection(currentPosition, M, distance));
		int pb;
		if (distance < 20) {
			switch (ct) {
				case Attack:
					dirWeight[initialDirection] += 3.0/distance;
					break;
				case Defense:
					dirWeight[initialDirection] += 2.0/distance;
					break;
				case Scope:
					if (poquemon(me()).scope < max_scope() )
						dirWeight[initialDirection] += 8.0/distance;
					break;
				case Stone:
					if (poquemon(me()).stones < max_stone() )
						dirWeight[initialDirection] += 10.0/distance;
					break;
				case Point:
					pb = points_value(currentPosition);
					dirWeight[initialDirection] += (pb/20.0)/distance;
					break;
				default:
					break;
			}
		}
	}

	Dir getInitialDirection (const Pos currentPosition, map<Pos, pair<Pos,Dir> >& M, int& distance) {
		pair<Pos,Dir> p = M[currentPosition];
		while(p.first != poquemon(me()).pos /*and distance < 20*/) {
			++distance;
			p = M[p.first];
		}
		return p.second;
	}

	int dir2num (Dir d) {
		switch(d) {
			case Top: return 0;
			case Bottom: return 1;
			case Left: return 2;
			case Right: return 3;
			case None: return -1;
		}
		return -1;
	}

};

/**
 * Do not modify the following line.
 */
RegisterPlayer(PLAYER_NAME);
