/**
 * @author L. BAFFAIT & G.MAROUSE
 * Jeu de Marienbad joueur contre IA
 */
class  MarienbadJvsO_MAROUSE_BAFFAIT {
	void principal(){
		test();
		jouerUnePartie();
	}

// ############### FONCTIONS ################ 

//Fonctions de jeu
	/**
	 * Fonction qui permet de jouer une partie
	 */
	void jouerUnePartie(){
		//INITIALISATION DU JEU
		//Déclaration des variables
		String joueur;
		int difficulte;
		int lignes;
		char c;
		boolean tour;
		int[] coup = new int[2];
		
		//Affectations par l'utilisateur
		// Saisie du nom du joueur
		joueur = SimpleInput.getString ("Entrez le nom du joueur :");
		
		// Saisie de la difficulté
		do{
			difficulte = SimpleInput.getInt ("Choisissez la difficulte | 1 | 2 | 3 | : ");
		}while (difficulte != 1 && difficulte != 2 && difficulte != 3);
		
		// Saisie du nombre de ligne du plateau
		do{
			lignes = SimpleInput.getInt ("Sur combien de lignes voulez-vous jouer entre 2 et 15 ? ");
		}while (lignes < 2 || lignes > 15);
		System.out.println ("La partie se jouera donc sur " + lignes + " lignes.\n");
		
		// Choix du premier à jouer
		do{
			c = SimpleInput.getChar ("Qui commence ? Saisir 'J' pour le joueur, 'O' pour l'ordinateur et 'A' pour aleatoire ");
		}while (c != 'A' && c!='J' && c!='O');
		if (c=='A'){
			tour = Math.random()<0.5;
		}else if (c=='J') {
			tour = true;
		}else{
			tour = false;
		}

		//Crétion du plateau
		int[] jeu = new int[lignes];
		for (int i = 0 ; i < lignes ; i++){
			jeu[i] = (i * 2) + 1;
		}
		int totalAlumettes = lignes*lignes;

		//BOUCLE DE JEU
		while (totalAlumettes > 0){
			//Affichage du plateau
			afficherPlateau(jeu);
			
			if (tour){
				//Joueur 1
				// Affichage
				System.out.println(joueur + " Joue : ");
				// Saisie de la ligne
				do {
					coup[0] = SimpleInput.getInt("Sur quel ligne souhaitez vous jouer ?");
				}while (coup[0]<0 || coup[0]>=jeu.length || jeu[coup[0]]==0);
				// Saisie du nombre d'alumettes'
				do {
					coup[1] = SimpleInput.getInt("Combien d'allumettes souhaitez vous prendre ?");
				}while (coup[1]<=0 || coup[1]>jeu[coup[0]]);
				// Mise a jour de l'état du jeu
				totalAlumettes -=jouerUnCoup(jeu,coup);
				
			}else{
				//IA
				// Calcul du coup à jouer selon le niveau de difficulté
				if (difficulte == 3){
					coup = niveau3(jeu);
				}else if (difficulte == 2){
					coup = niveau2(jeu);
				}else {
					coup = niveau1(jeu);
				}
				// Mise a jour de l'état du jeu
				totalAlumettes -=jouerUnCoup(jeu, coup);
				// Affichage
				System.out.println("L'IA a prise "+ coup[1] +"alumettes a la ligne "+coup[0]);
				
			}
			//Changement de joueur
			tour = !tour;
		}
		
		//FIN
		if (tour){
			System.out.println(joueur + "! Tu as perdu. Retente ta chance !");
		}else{
			System.out.println(joueur + "! Tu as gagne !");
		}
	}

    /**
     * Affiche le plateau de jeu
     * @param t le tableau représentatif du plateau de jeu
     */
    void afficherPlateau(int[] t){
        for (int i=0;i<t.length;i++){
			System.out.print(i+" : ");
            for(int j=0;j<t[i];j++){
                System.out.print("|");
            }
            System.out.println();
        }
    }   
    
    /**
     * Un joueur joue un coup
     * @param t le tableau représentatif du plateau de jeu
     */
    int jouerUnCoup(int[] t, int[]coup){
		t[coup[0]]-=coup[1];
		return coup[1];
    }
    
    /**
	* Teste la méthode jouerUnCoup()
	*/
	void testJouerUnCoup(){
		System.out.println ();
		System.out.println ("*** testJouerUnCoup ");
		testCasJouerUnCoup (new int[] {1,3,5,7},new int[] {0,1}, new int[] {0,3,5,7});
		testCasJouerUnCoup(new int[] {0,0,0,7},new int[] {3,7},new int[] {0,0,0,0});
		testCasJouerUnCoup(new int[] {1,0,0,0,0,5,0,0,0,8,0,0,0,22,0},new int[] {13,10},new int[] {1,0,0,0,0,5,0,0,0,8,0,0,0,12,0});
	}
	/**
	* teste un appel de jouerUnCoup()
	* @param jeu un plateau de jeu à tester
	* @param result le résultat attendu
	*/
	void testCasJouerUnCoup (int[] jeu, int[] coup, int[] result) {
		// Affichage
		System.out.print ("juerUnCoup(");
		displayTab(jeu);
		System.out.print(") \t= ");
		displayTab(result);
		System.out.print("\t:\t");
		// Appel
		jouerUnCoup(jeu,coup);
		// Verification
		if (compare(jeu,result)){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}

//Booléen de victoire & ses tests    
	/**
	 * Détermine si la victoire est possible en 1 coup
	 * @param jeu le plateau de jeu
	 * @return renvoi true si la victoire est possible sinon false
	 */
	boolean victoire(int[] jeu){
		boolean victoire = false;
		int cpt=0;
		for (int i = 0; i<jeu.length;i++){
			if (jeu[i]==0){
				cpt+=1;
			}
		}
		if (cpt == jeu.length-1){
			victoire = true;
		}
		return victoire;
	}
	    /**
	* Teste la méthode victoire()
	*/
	void testVictoire(){
		System.out.println ();
		System.out.println ("*** testVictoire() ");
		testCasVictoire (new int[] {1,0,0,0},true);
		testCasVictoire(new int[] {0,0,0,7},true);
		testCasVictoire(new int[] {0,0,0,0,0,0,0,0,0,0,0,0,0,22,0},true);
	}
	/**
	* teste un appel de victoire()
	* @param jeu un plateau de jeu à tester
	* @param result le résultat attendu
	*/
	void testCasVictoire (int[] jeu , boolean result) {
		// Affichage
		System.out.print ("niveau1(");
		displayTab(jeu);
		System.out.print(") \t= " + result + "\t : ");
		// Appel
		boolean resExec = victoire(jeu);
		// Verification
		if (resExec==result){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}

//Difficulté niveau 1 (Aléatoire)
    /**
     * IA joue un coup au hasard
     * @param jeu le plateau
     * @return le coup jouer dans un tableau
     */
    int[] niveau1(int[] jeu) {
		int ligne=0;
		int nb = 0;
		do {
			ligne = (int) (Math.random()*jeu.length);
		}while (ligne<0 || ligne>=jeu.length || jeu[ligne]==0);
		do {
			nb = (int)(Math.random()*(jeu[ligne]+1));
		}while (nb<=0 || nb>jeu[ligne]);
		return new int[] {ligne,nb};
    }
    
//Difficulté niveau 2 (Aléatoire + victoire) avec ses tests
    /**
     * IA joue un coup au hasard mais gagne si elle peut
     * @param jeu le plateau
     * @return le coup jouer dans un tableau
     */
    int[] niveau2(int[] jeu) {
		boolean victoire = victoire(jeu);
		int[] coup = new int[2];
		
		if (victoire) {
			for (int i =0; i<jeu.length; i++) {
				if (jeu[i]!=0){
					coup[0]=i;
					coup[1]=jeu[i];
				}
			}
		} else {
			coup = niveau1(jeu);
		}
		return coup;
    }
    
    /**
	* Teste la méthode niveau2()
	*/
	void testNiveau2(){
		System.out.println ();
		System.out.println ("*** testNiveau2() ");
		testCasNiveau2 (new int[] {1,0,0,0},new int[] {0,1});
		testCasNiveau2 (new int[] {0,0,0,7},new int[] {3,7});
		testCasNiveau2 (new int[] {0,0,0,0,0,0,0,0,0,0,0,0,0,22,0},new int[] {13,22});
	}
	/**
	* teste un appel de niveau2()
	* @param jeu un plateau de jeu à tester
	* @param result le résultat attendu
	*/
	void testCasNiveau2 (int[] jeu , int[] result) {
		// Affichage
		System.out.print ("niveau2(");
		displayTab(jeu);
		System.out.print(") \t= ");
		displayTab(result);
		System.out.print("\t:\t");
		// Appel
		int[] resExec = niveau2(jeu);
		// Verification
		if (compare(resExec,result)){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}
    

//Difficulté niveau 3 (Stratégie gagnante) et ses tests
     /**
     * IA applique la stratégie gagnante
     * @param jeu le plateau
     * @return le coup jouer dans un tableau
     */
    int[] niveau3(int[] jeu) {
		boolean victoire = victoire(jeu);
		int[] coup = new int[2];
		
		if (victoire) {
			for (int i =0; i<jeu.length; i++) {
				if (jeu[i]!=0){
					coup[0]=i;
					coup[1]=jeu[i];
				}
			}
		} else {
			String[] tableauBinaire = new String[jeu.length];
			// comme on a max 31 allumettes, on encode ça sur 5 bits
			for (int ligne = 0; ligne < jeu.length; ligne++) {
			// Essayer chaque nombre d'allumettes à retirer
				for (int nbAllumettes = 1; nbAllumettes <= jeu[ligne]; nbAllumettes++) {
					int[] copie = copierTab(jeu);	// Créer une copie du plateu de jeu pour évaluer le coup
					copie[ligne] -= nbAllumettes;  // Retirer les allumettes sur la ligne actuelle
					
					// Remplir le tableau binaire
					for (int i = 0; i < copie.length; i++) {    
						String binaire = Integer.toBinaryString(copie[i]);
						int len = 5 - binaire.length();
						for (int j=0; j<len;j++){
							binaire = "0" + binaire;
						}
						tableauBinaire[i] = binaire;
					}
					
					// Calculer la somme par colonne
					int[] sommeParColonne = new int[5];
					for (int i = 0; i < jeu.length; i++) {
						for (int j = 0; j < 5; j++) {
							sommeParColonne[j] += tableauBinaire[i].charAt(j);
						}
					}
								// Vérifier si le coup est gagnant
					boolean gagnant= true;
					int i =0;
					while (i<sommeParColonne.length && gagnant){
						if(sommeParColonne[i] % 2 != 0){
							gagnant=false;
						}
						i++;
					}
					if (gagnant) {
					// Si c'est un coup gagnant et qu'il enlève plus d'allumettes, on le sauvegarde
						if (nbAllumettes > coup[1]) {
							coup[0] = ligne; 
							coup[1] = nbAllumettes;
						}
					}
				}
			}
			// Si on ne trouve pas de coup gagnant on joue un coup aléatoire
			if(coup[1]==0){
				coup = niveau1(jeu);
			}
		}
		return coup;
    }
	/**
	* Teste la méthode niveau3()
	*/
	void testNiveau3(){
		System.out.println ();
		System.out.println ("*** testNiveau3() ");
		testCasNiveau3 (new int[] {1,3,5,7,9},new int[] {4,9});
		testCasNiveau3 (new int[] {1,3,3},new int[] {0,1});
		testCasNiveau3 (new int[] {1,2,3,4},new int[] {3,4});
		testCasNiveau3 (new int[] {1,1,1,1,1},new int[] {0,1});
		testCasNiveau3 (new int[] {1,3,0,1,7,2},new int[] {4,6});
	}
	/**
	* teste un appel de niveau3()
	* @param jeu le tebleau d'entier représentatif du plateau de jeu
	* @param result le résultat attendu
	*/
	void testCasNiveau3 (int[] jeu , int[] result) {
		// Affichage
		System.out.print ("niveau3(");
		displayTab(jeu);
		System.out.print(") \t= ");
		displayTab(result);
		System.out.print("\t:\t");
		// Appel
		int[] resExec = niveau3(jeu);
		// Verification
		if (compare(resExec,result)){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}
	
	/**
	 * Fonctions qui crée une copie d'un tableau d'int
	 * @param t un tableau d'entiers
	 * @return renvoi la copie du tableau donné en parametre
	 */
	int[] copierTab(int[] t){
		int[] copie = new int[t.length];
		for (int i=0; i<t.length; i++){
			copie[i] = t[i];
		}
		return copie;
	}
	/**
	* Teste la méthode copierTab()
	*/
	void testCopierTab(){
		System.out.println ();
		System.out.println ("*** testCopierTab() ");
		testCasCopierTab (new int[] {0,0,0},new int[] {0,0,0});
		testCasCopierTab (new int[] {},new int[] {});
		testCasCopierTab (new int[] {5},new int[] {5});
		testCasCopierTab (new int[] {5,3,2,7,9},new int[] {5,3,2,7,9});
	}
	/**
	* teste un appel de copierTab()
	* @param t un tableau d'entier
	* @param result le résultat attendu
	*/
	void testCasCopierTab (int[] t , int[] result) {
		// Affichage
		System.out.print ("copierTab(");
		displayTab(t);
		System.out.print(") \t= ");
		displayTab(result);
		System.out.print("\t:\t");
		// Appel
		int[] resExec = copierTab(t);
		// Verification
		if (compare(resExec,result)){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}
	
    // ########## Comparison de tableau ########## 
	/**
	* vérifie si deux tableaux sont identiques
	* @param tab1 premier tableau
	* @param tab2 deuxième tableau
	* @return vrai si les tableaux tab1 et tab2 sont identiques, faux sinon
	*/
	boolean compare(int[] tab1, int[] tab2){
		boolean val = true;
		if (tab1.length == tab2.length){
			int i=0;
			while (i<tab1.length && val){
				if (tab1[i] != tab2[i]){
					val = false;
				}
				i++;
			}
		}else {
			val=false;
		}
		return val;
	}
	/**
	* Teste la méthode compare()
	*/
	void testCompare () {
		System.out.println ();
		System.out.println ("*** testCompare ");
		testCasCompare(new int[]{},new int[]{},true);
		testCasCompare (new int[]{},new int[]{1,2,3},false);
		testCasCompare (new int[]{1,2,3},new int[]{1,2,3,4},false);
		testCasCompare (new int[]{0},new int[]{0},true);
		testCasCompare(new int[]{20,7,3,10,6},new int[]{20,7,3,10,6},true);
		testCasCompare (new int[]{0,-2,4},new int[]{1,2,-4},false);

	}
	/**
	* teste un appel de compare
	* @param t1 premier tableau
	* @param t2 deuxième tableau
	* @param result le résultat attendu
	*/
	void testCasCompare (int[] t1,int[] t2, boolean result) {
		// Affichage
		System.out.print ("compare(");
		displayTab(t1);
		System.out.print (", ");
		displayTab(t2);
		System.out.print(") \t= " + result + "\t : ");
		// Appel
		boolean resExec = compare(t1,t2);
		// Verification
		if (resExec == result){
			System.out.println ("OK");
		} else {
			System.err.println ("ERREUR");
		}
	}
	// ########## Affichage de tableau ########## 
	/**
	 * Affiche le tableau t
	 * @param t tableau d'entier
	 */
	void displayTab(int[] t) {
		int i = 0;
		System.out.print("{");
		while(i<t.length-1){
			System.out.print(t[i] + ",");
			i++;
		}
		if(t.length>=1){
			System.out.print(t[i]+"}");
		}else{
			System.out.print("}");
		}
	}
	
	//TESTS
	/**
	 * Fonctiion qui execute les différents méthode de test du programme
	 */
	void test() {
		testCompare();
		testCopierTab();
		testJouerUnCoup();
		testVictoire();
		testNiveau2();
		testNiveau3();
		System.out.println("\n###########################################################\n\n");
	}
}
