
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

public class Champion extends Api
{
	public static int playerId;
	
	private Map map = new Map();
	private int collectDuckId; 
	private int improverDuckId;
	
	
	private int[] collectDuckTarget = (new int[]{25, 45, 10});
	private int[] improverDuckTarget = (new int[]{100, 20, 50});

	
	
    /**
     * Fonction appelée au début de la partie.
     */
    public void partie_init()
    {
    	playerId = moi();
    	collectDuckId = troupes_joueur(Api.moi())[0].id;
    	improverDuckId = troupes_joueur(Api.moi())[1].id;
    	map.init();
    	
    	Goal.preInit(new int[] {collectDuckId, improverDuckId}, map);

    	//Goal.search(collectDuckId, collectDuckTarget);
    	//Goal.search(improverDuckId, improverDuckTarget);
    	
    }

    /**
     * Fonction appelée à chaque tour.
     */
    public void jouer_tour()
    {
    	Goal.init(); //chargement des possibilites du tour
    	collecter();  //troupe 1
    	improver(); //troupe 2
    	
    	Goal.tunnelBuilding(); //construction tunnel
    }

    /*
     * Mouvements de la premiere troupe
     */
    private void improver() {
    	int remains = 5; //nb actions
    	
    	//objectif en vue ?	
    	Goal.search(improverDuckId, improverDuckTarget); //recherche d'un objectif
    	
    	//
    	Goal.debug(improverDuckId, PigeonDebug.PIGEON_BLEU);
    	//
    	
    	if (tour_actuel() > 10 && tour_actuel()%4 == 0) {
    		grandir(improverDuckId);
    		remains-=3;
    		
    		if (tour_actuel() < 90) {
	    		//changement du comportement 
	    		improverDuckTarget[0]-=1; //moins vers les nid
	    		improverDuckTarget[1]+=1; //+ pains
	    		improverDuckTarget[2]+=1; //+ attaques
    		}
    	}
    	
    	for (int i = 0; i < 5; i++) {
    		if (Goal.progress(improverDuckId, 1)){ //avance vers l'objectif
    			remains--;
    		}
    	}
    	
    	
    	
    	//action non utilisees ?
    	if (remains>0)
    		if (remains>=3) {
    			grandir(improverDuckId);
    			remains-=3;
    			if (remains>0)
    				move(Api.troupes_joueur(playerId)[1].dir, improverDuckId, 1, remains);
    		}
    		else
    			move(Api.troupes_joueur(playerId)[1].dir, improverDuckId, 1, remains);
	    
    	
    }
    
    
    /*
     * Mouvements de la seconde troupe
     */
    private void collecter() {
    	int remains = 5;//nb actions
    	
    	//objectif en vue ?	
    	Goal.search(collectDuckId, collectDuckTarget); //recherche d'un objectif
    	
    	Goal.debug(collectDuckId, PigeonDebug.PIGEON_JAUNE);
    	
    	//grandit tous les 5 tours
    	if (tour_actuel()%5 == 0) {
    		grandir(collectDuckId);
    		remains-=3;
    	}
    	
		
    	for (int i = 0; i < 5; i++) {
    		if (Goal.progress(collectDuckId, 0)) //avance vers l'objectif (max 5 deplacements)
    			remains--;
    		
    	}
    	
    	//actions non utilisees ?
    	if (remains>0) {
    		if (remains>=3) {
    			grandir(collectDuckId);
    			remains-=3;
    			if (remains>0)
    				move(Api.troupes_joueur(playerId)[0].dir, collectDuckId, 0, remains);
    		}
    		else
    			move(Api.troupes_joueur(playerId)[0].dir, collectDuckId, 0, remains);
    	}
    		
	    
	}
   
   
    public static void move(Direction currentDir, int id, int id2, int actions) {
    	Direction dir = currentDir;
    	Position pos = troupes_joueur(Api.moi())[id2].maman;
    	
    	for (int i = 0; i < actions; i++)
    	{
    		
    		for (int j = 0; j < 4; j++) {
    			
    			Direction dir2 = null;
    			
    			//afficher_position(pos);
    			Position pos2 = newPosition(pos);
    			if (dir == Direction.NORD) {
    				pos2.ligne++;
    				dir2 = Direction.EST;
    			}
    			else if (dir == Direction.SUD) {
    				pos2.ligne--;
    				dir2 = Direction.OUEST;
    			}
    			else if (dir == Direction.OUEST) {
    				pos2.colonne--;
    				dir2=Direction.NORD;
    			}
    			else {
    				pos2.colonne++;
    				dir2 = Direction.SUD;
    			}
    			
    			
    			
    			if (j!=3 && pos2.colonne > 0 && pos2.ligne > 0 && pos2.colonne < LARGEUR-1 && pos2.ligne < HAUTEUR -1 && isEmpty(pos2)) {
    				break;
    			} else
    				dir = dir2;
    			
    		}
    		
    		
    		avancer(id, dir);
    	
    	}
    		
    	
    	
	}

	

	/**
     * Fonction appelée à la fin de la partie.
     */
    public void partie_fin()
    {

    	System.out.println("\33[5mheyyy");
    
    }
    
    public static Position newPosition(Position pos) {
    	Position p = new Position();
    	
    	p.colonne = pos.colonne;
    	p.ligne = pos.ligne;
    	p.niveau = pos.niveau;
    	
    	return p;
    }
    
    public static Position inFront(Direction d, Position p) {
    	Position pos = newPosition(p);
    	if (d == Direction.NORD) {
    		pos.ligne++;
    	}
    	
    	else if (d == Direction.SUD) {
    		pos.ligne--;
    	}
    	
    	else if (d == Direction.OUEST) {
    		pos.colonne--;
    	}
    	
    	else if (d == Direction.EST) {
    		pos.colonne++;
    	}
    	
    	return pos;
    }

    public static boolean isEmpty(Position pos) {
    	if (!(pos.ligne > 0 && pos.ligne < HAUTEUR-1 && pos.colonne > 0 && pos.colonne < LARGEUR-1))
    		return false;
    		
    	
    	if (info_case(pos).contenu == TypeCase.BUISSON || info_barriere(pos) == EtatBarriere.FERMEE)
    		return false;
    	for (Troupe t : troupes_joueur(playerId)) {
    		for (Position p : t.canards) {
    			if (p.ligne == pos.ligne && p.colonne == pos.colonne && p.niveau == pos.niveau)
    				return false;
    		}
    	}
    	for (Troupe t : troupes_joueur(adversaire())) {
    		for (Position p : t.canards) {
    			if (p.ligne == pos.ligne && p.colonne == pos.colonne && p.niveau == pos.niveau)
    				return false;
    		}
    	}
    	
    	
    	
    	return true;
    }
    
    public static Direction[] cherche_chemin(Position start, Position end) {
    	
/*
    	
    	Position p1 = newPosition(start); //EST
    	Position p2 = newPosition(start); //OUEST
    	Position p3 = newPosition(start); //SUD
    	Position p4 = newPosition(start); //NORD
    	p1.colonne++;
    	p2.colonne--;
    	p3.ligne++;
    	p4.ligne--;
    	
    	afficher_position(p1);
    	afficher_position(p2);
    	afficher_position(p3);
    	afficher_position(p4);
    	
    	Direction[] dir = new Direction[0];
    	Direction d1 = Direction.NORD;
    	
    	
    	
    	currentTargetScore = score&& isEmpty(p1)) {
    		System.out.println(1);
    		Direction[] dir2 = trouver_chemin(p1, end);
    		if (dir2.length > 0 && (dir.length == 0 || dir2.length < dir.length)) {
    			dir = dir2;
    			d1=Direction.EST;
    		}
    	}
    	
		if (p2.colonne > 0 && isEmpty(p2)) {
    		System.out.println(2);
			Direction[] dir2 = trouver_chemin(p2, end);
			if (dir2.length > 0 && (dir.length == 0 || dir2.length < dir.length)) {
    			dir = dir2;
    			d1=Direction.OUEST;
    		}
		}
		
		if (p3.ligne < HAUTEUR-1 && isEmpty(p3)) {
    		System.out.println(3);
			Direction[] dir2 = trouver_chemin(p3, end);
			System.out.println(dir2.length);
			if (dir2.length > 0 && (dir.length == 0 || dir2.length < dir.length)) {
    			dir = dir2;
    			d1=Direction.NORD;
    		}
		}
		
		if (p4.ligne > 0 && isEmpty(p4)) {
    		System.out.println(4);
			Direction[] dir2 = trouver_chemin(p4, end);
			if (dir2.length > 0 && (dir.length == 0 || dir2.length < dir.length)) {
    			dir = dir2;
    			d1=Direction.SUD;
    		}
		}
		    	
    	
    	Direction[] d = new Direction[5];
    	d[0] = d1;
    	for (int i = 1; i < 5; i++) {
    		d[i] = dir[i-1];
    	}*/
    	
    	
    	if (isEmpty(end))
    		return trouver_chemin(start, end);
    	else
    		return new Direction[0];
    }
    
}

class Map {

	public final List<Position> papis = new ArrayList<>();
	public final List<Position> nests = new ArrayList<>();
	public List<Position> holes = new ArrayList<>();
	
	public void init() {
		Position pos = new Position();
		for (int x = 0; x < Api.LARGEUR; x++) {
			pos.colonne = x;
			for (int y = 0; y < Api.HAUTEUR; y++) {
				pos.ligne=y;
				EtatCase et = Api.info_case(pos);
				if (et.contenu == TypeCase.PAPY) {
					papis.add(et.pos);
				} else if (et.contenu == TypeCase.NID) {
					nests.add(et.pos);
				} else if (et.contenu == TypeCase.TROU) {
					holes.add(et.pos);
				}
			}
		
		}
		
	
	}
}

/*class Tunnel {
	
	final Position p1;
	final Position p2;
	final int distance;
	
	Position toDestroyPos;
	
	
	public Tunnel(Position p1, Position p2) {
		this.p1 = p1;
		this.toDestroyPos = Champion.newPosition(p1);
		toDestroyPos.niveau=-1;
		this.p2 = p2;
		distance = p1.colonne - p2.colonne + p1.ligne - p2.ligne;
	}
	
}
*/


enum Targets {
	NEST(new ScoreRun() {
		@Override
		public float run(Troupe trp, int[] t, Position pT) {
			EtatNid et = Api.info_nid(pT);
			
			if (et == EtatNid.LIBRE) { //libre
				return 2*t[0]/100f;
			}
			
			else if ((Champion.playerId == 0 && et == EtatNid.JOUEUR_0) || (Champion.playerId == 1 && et == EtatNid.JOUEUR_1)) { //appartient joueur
				return (t[0] * trp.inventaire/2f)/100f;
			} 
			
			else { //appartient adversaire 
				return 0;
			}
		
		}
	}),
	BREAD(new ScoreRun() {
		@Override
		public float run(Troupe trp, int[] t, Position pT) {
			//troupe inventaire disponible
			int max = Api.inventaire(trp.taille);
			if (max < 1)
				max = 1;
			
			return ((max - trp.inventaire)*(max - trp.inventaire))*(t[1]/100f);
		}
	}),
	ENNEMY(new ScoreRun() {
		@Override
		public float run(Troupe trp, int[] t, Position pT) {
			
			int ch = Champion.trouver_chemin(trp.maman, pT).length;
			if (ch < 5) {
				return t[2]/10f;
			}
			else {
				return t[2]/1000f;
			}
			
		}
	});
	
	final ScoreRun runMethod;
	
	Targets(ScoreRun run) {
		this.runMethod = run;
	}

	interface ScoreRun {
		public float run(Troupe trp, int[] t, Position pT);
	}
}



class Goal{
	
	
	private static java.util.Map<Integer, Goal> goals;
	private static Map map;
	
	private static Position currentTunnelBuilding;
	private static Position currentTunnelBuilding2;

	
	public Score currentTargetScore;
	public boolean has;
	public Direction[] path;
	public int progress = 0;
	
	final int id1;
	final int id2;
	
	private Goal(int i1, int i2) {
		this.id1 = i1;
		this.id2 = i2;
	}





	public static void debug(int id, PigeonDebug pigeon) {
		Goal g = goals.get(id);
		if (g.currentTargetScore != null)
			Api.debug_poser_pigeon(g.currentTargetScore.position, pigeon);
		
	}
	
	public static void preInit(int[] ids, Map map) {
		
		goals = new HashMap<>();
		for (int i = 0; i < ids.length; i++) {
			goals.put(ids[i], new Goal(ids[i], i));
		}
		Goal.map = map;
		
	}
	
	
	private static List<Score> scores = new ArrayList<>();
	
	
	public static void init() {
		
		//Recherche point interessants:
		scores.clear();
		
		//PAINS
		for (Position pos : map.papis) {
			Score sc = new Score(Targets.BREAD, pos, Api.papy_tours_restants(pos));
			sc.nb = Api.info_case(pos).nb_pains;
			scores.add(sc);
		}
		for (Position pos : Api.pains()) {
			EtatCase et = Api.info_case(pos);
			if (et.contenu != TypeCase.PAPY) {
				Score sc = new Score(Targets.BREAD, pos, 0);
				sc.nb = et.nb_pains;
				scores.add(sc);
			}
		}
		
		
		
		//NIDS
		for (Position pos : map.nests) {
			Score sc = new Score(Targets.NEST, pos, 0);
			sc.nb = 1;
			scores.add(sc);
		}
		
		
		//ENNEMIES
		for (Troupe trp : Api.troupes_joueur(Api.adversaire())) {
			
			Score sc = new Score(Targets.ENNEMY, Champion.inFront(trp.dir, Champion.inFront(trp.dir, trp.maman)), 0);
			sc.nb = 1;
			scores.add(sc);
		}
		
		
		//SOUS SOL
		if (currentTunnelBuilding == null) {
			
			
			for (Position p1 : map.holes) {
				
			}
			
			int max = -1;
			for (Position p2 : map.holes) {
				
				if (!p1.equals(p2)) {
					
					int d1 = Math.abs(p1.colonne - p2.colonne) + Math.abs(p1.ligne - p2.ligne);
					int d2 =  Api.trouver_chemin(p1, p2).length;
					
					if (d2 == 0) {
						currentTunnelBuilding = p1;
						currentTunnelBuilding2 = p2;
						break;
					} else {
						
						if (max == -1 || d2 - d1 > max) {
							currentTunnelBuilding = p1;
							currentTunnelBuilding2 = p2;
							max = d2 - d1;
						}
						
					}
				}
			
				
				
			}
			
			
			
			currentTunnelBuilding = Champion.newPosition(currentTunnelBuilding);
			currentTunnelBuilding.niveau=-1;
		}
		
		
	}
	

	public static void search(int id, int[] target){
		goals.get(id).search(target);
	}


	public static void tunnelBuilding() {
		Api.creuser_tunnel(currentTunnelBuilding);
		do {
		
			int dX = currentTunnelBuilding2.colonne - currentTunnelBuilding.colonne;
			if (dX > 0) {
				currentTunnelBuilding.colonne++;
			} else if (dX < 0) {
				currentTunnelBuilding.colonne--;
			}
			else {
				
				int dY = currentTunnelBuilding2.ligne - currentTunnelBuilding.ligne;
				if (dY > 0) {
					currentTunnelBuilding.ligne++;
				} else if (dY < 0) {
					currentTunnelBuilding.ligne--;
				} else {
					currentTunnelBuilding = null;
					return;
					
				}
			}
				
		} while(Api.info_case(currentTunnelBuilding).contenu == TypeCase.TUNNEL);
		
	}

	
	
	public void search(int[] target) {
		
		
		Troupe trp = Api.troupes_joueur(Champion.playerId)[id2];
		Position currentPos = trp.maman;

		float max = 0;
		int idMax = -1;
		for (int i = 0; i < scores.size(); i++) {
			Score sc = scores.get(i);
			
			//System.out.println(sc.type + "--" + max);
			int dist = Champion.cherche_chemin(currentPos, sc.position).length;
			//System.out.println(dist);
			
			if (dist > 0) { 
				float scoreV = ((Api.LARGEUR+Api.HAUTEUR)/(dist/5f + sc.time))/dist*(sc.nb/10f);
				//System.out.println(scoreV);
				
				scoreV*= sc.type.runMethod.run(trp, target, sc.position);
				//System.out.println(scoreV);
				
				if (idMax == -1 || scoreV > max) {
					max = scoreV;
					idMax = i;
				}
			}
		}
		
		
		//System.out.println(currentPos);
		//System.out.println(pMin);
		if (idMax != -1) {
			currentTargetScore = scores.get(idMax);
			path = Champion.cherche_chemin(currentPos, currentTargetScore.position);
			
			
			has = true;
			progress=0;
		}
		
	
	}
	
	
	
	public static boolean progress(int id, int id2) {
		
		Goal g = goals.get(id);
		
		if (g.path != null && g.progress < g.path.length) {
			Champion.move(g.path[g.progress], id, id2, 1);
			g.progress++;
			return true;
		}
	
		return false;

	
	}
}

class Score {
	Targets type;
	Position position;
	int time; //temps avant apparition
	int distance;
	int nb = 0;
	
	public Score(Targets target, Position pos, int time) {
		type = target;
		position = pos;
		this.time = time;
	}
}

	



