
#include "Player.hh"

using namespace std;


/**
 * Write the name of your player and save this file
 * as AI<name>.cc
 */
#define PLAYER_NAME Uli





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.
     */     
  
  Pos normalizePosition(const Pos& p) {
    int row = first(p);
    int u = number_universe_columns();
    int col = second(p)%u;
    if (col < 0) col += u;
    return {row,col};
  }

  bool can_visit_position(const Pos& p, int round) {
    if (not within_window(p,round)) return false;
    if (cell(p).type == TYRE or cell(p).type == CAR) return false;
    return true;
  }

  bool can_move_from_position(Pos& p, Dir d, int round) {
    for (auto dir : dir2all.at(d)) {
      Pos newPos = normalizePosition(p + dir);
      if (not within_window(newPos,round)) return false;
      if (cell(newPos).type == TYRE or cell(newPos).type == CAR) return false;
    }
    return true;
  }

  bool ahead_and_close(int left, int right){
    return (right > left and right <= left + 3);
  }
    
  bool can_attack_from_position(int cid, Pos& p) {
    for (int id = 0; id < number_cars(); ++id){
      if (car(id).alive) {
	if (player_of(id) != me() and
	    first(car(id).pos) == first(p) and
	    ahead_and_close(second(p), second(car(id).pos))) {
	  //	  cerr << "Shoot from car " << cid << " at position " << first(p) << " " << second(p) << " to car " << id << " at position " << first(car(id).pos) << " " << second(car(id).pos) << endl;
	return true;
	}
      }
    }
    return false;
  }

  bool tyre_in_front(Car_Id cid) {
    Pos p = car(cid).pos;
    if (cell(normalizePosition(p+DEFAULT)).type == TYRE) return true;
    if (cell(normalizePosition(p+SLOW)).type == TYRE) return true;
    if (cell(normalizePosition(p+FAST)).type == TYRE) return true;
    return false;
  }

  bool threatened_by_missile(Car_Id cid, Pos pos){
    return (cell(pos).type == MISSILE or
	    cell(pos-FAST).type == MISSILE or
	    cell(pos-DEFAULT).type == MISSILE);
  }

  // Shortest path
  bool shortest_path(const Pos& ini, const set<CType>& goals, int currentRound, Pos& nextPos){
    const int    infinite = numeric_limits<int>::max();
    vector<vector<int>> dist(number_rows(),vector<int>(number_universe_columns(),infinite));
    vector<vector<Pos>> prev(number_rows(),vector<Pos>(number_universe_columns(),{-1,-1}));
    queue<Pos> q;
    dist[first(ini)][second(ini)] = 0;
    q.push(ini);

    //    cerr << "Starts shortest path at " << first(ini) << " " << second(ini) << endl;
    while (not q.empty()){
      Pos p = q.front(); q.pop();

      if (goals.contains(cell(p).type)) {
	// Found one goal. Traverse prev and compute nextPos
	//cout << "Found goal in pos " << first(p) << " " << second(p) << endl;
	Pos p_in_path = p;
	while (prev[first(p_in_path)][second(p_in_path)] != ini) {
	  p_in_path = prev[first(p_in_path)][second(p_in_path)];
	}
	nextPos = p_in_path;
	return true;
      }

      //      cerr << "popped " << first(p) << " " << second(p) << endl;
      for (auto& d : dir2all) {
	Pos nextPos = normalizePosition(p + d.first);
	//	cerr << "Neighbour: " << first(nextPos) << " " << second(nextPos) << endl;	
	//	cerr << "Neighbour normalized: " << first(nextPos) << " " << second(nextPos) << endl;
	if (can_move_from_position(p,d.first,currentRound + dist[first(p)][second(p)] + 1) and
	    dist[first(nextPos)][second(nextPos)] == infinite){	    
	  dist[first(nextPos)][second(nextPos)] = dist[first(p)][second(p)] + 1;
	  prev[first(nextPos)][second(nextPos)] = p;
	  q.push(nextPos);
	}
      }
    }
    return false;
  }

  
    /**
     * 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 () {

      /* 

	 Si tinc coet darrera (4 o 6 posicions) desplacem [compte no xocar]
	 Si tinc cotxe darrera (4 posicions) desplacem [compte no xocar]
	 Busco objectiu [gas, aigua, coet] depenent del que tingui
	   Una funcio que donada una llista d'objectius em retorni la llista de moviments a seguir (de fet amb el primer moviment ja en tinc prou)
	 

       */


      vector<vector<bool>> targeted(number_rows(),vector<bool>(number_universe_columns(),false));
      
      for (Car_Id cid = begin(me()); cid != end(me()); ++cid) {
	Car c = car(cid);
	Pos p = c.pos;

	
	if (c.alive) {
	  Pos next;
	  set<CType> goals;
	  
	  if      (c.nb_miss > 0 and tyre_in_front(cid)) shoot(cid);	  
	  else if (c.nb_miss > 0 and can_attack_from_position(cid,p)) shoot(cid);
	  else {
	    if      (c.gas <= 20)    goals = {GAS_BONUS};
	    else if (c.nb_miss <= 1) goals = {MISSILE_BONUS};
	    else                     goals = {MISSILE_BONUS,WATER_BONUS,GAS_BONUS};
	    if (shortest_path(p,goals,round(),next)
		and not targeted[first(next)][second(next)]
		and not threatened_by_missile(cid,next)
		){
	      Dir dir;
	      for (auto d : dir2all) {
		if (normalizePosition(p+d.first) == next) {dir = d.first; break;}
	      }	     
	      move(cid,dir);
	      targeted[first(next)][second(next)] = true;		      
	    }
	  }
	}
      }	
    }

    
};


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

