program Sphinx;

// Jeu du Sphinx, programmé dans le cadre du projet d'informatique du 1er semestre.
// Auteurs : INGOUFF Christian, RODRIGUEZ Sébastien
// Date de création : 11/01/12
// Version : 7.1 (21/01/12)
// Version : 8.0 (23/01/12) : Bot vs. bot et joueur vs. joueur

uses crt, sysutils;
type
	matrix = array of array of integer;
	matstring = array of array of string;
	coord = array[1..2] of integer;
	array_coord = array[1..4] of coord;
	array_itg = array[1..4] of integer;
	array_bool = array[1..4] of boolean;

//
//                                                                 //    DESIGN MENU    //
//

procedure commentJouerJeu();
// RODRIGUEZ Sébastien
// Instructions pour le jeu.
begin
	writeln('                                                    ------ Jeu ------');
	writeln(' ');
	writeln('     Le but du jeu est d''atteindre le tresor du labyrinthe. Vous cherchez à l''aveugle, seul votre point de départ');
	writeln('     et le trésor vous sont indiqués. Mais faites vite! Votre rival cherche lui aussi le trésor dans le labyrinthe ');
	writeln('             que vous avez dessiné pour lui. Trouvez votre trésor avant qu''il ne trouve le sien pour gagner!');
	writeln('');
	writeln('                                     Mur : ▓▓▓       Entrée : o       Trésor : X');
	writeln('');
	writeln('                                                 Personnage : ▲ ▼ ◄ ►');
end;

procedure commentJouerRegles();
// RODRIGUEZ Sébastien
// Instructions pour le jeu.
begin
	writeln(' ');
	writeln(' ');
	writeln('                                                   ------ Règles ------');
	writeln(' ');
	writeln('          Vous pouvez vous déplacer autant de fois que vous le souhaitez tant que vous ne rencontrez pas de mur.');
	writeln('           Lorsque vous rentrez dans un mur la main passe à votre rival jusqu''à ce qu''il trouve le trésor ou');
	writeln('                  qu''il rencontre lui même un mur. Le premier à atteindre le trésor remporte la partie.');
end;

procedure commentJouerCommandes();
// RODRIGUEZ Sébastien
// Instructions pour le jeu.
begin
	writeln('');
	writeln('');
	writeln('                                                  ------ Commandes ------ ');
	writeln('');
	writeln('                     ___');
	writeln('                    |   |');
	writeln('                    | ▲ |');
	writeln('                 ___|___|___          Les flèches directionnelles pour vous diriger.');
	writeln('                |   |   |   |');
	writeln('                | ◄ | ▼ | ► |');
	writeln('                |___|___|___|');
	writeln('');
	writeln('                   ______');
	writeln('                  |Entrée|');
	writeln('                  |_<---┘|                    La touche Entrée pour valider.');
	writeln('                    |    |');
	writeln('                    |____|');

end;


procedure commentJouer();
// RODRIGUEZ Sébastien
// Instructions pour le jeu. Ce qui est retourné quand "Règles du jeu" est sélectionné dans le menu principal.
begin
writeln(' ');
	commentJouerJeu();
	commentJouerRegles();
	commentJouerCommandes();
	writeln('');
	writeln('                                 - Appuyez sur ECHAP ou RET. ARRIERE pour retourner au menu. -');
end;

procedure defeatScreen();
// RODRIGUEZ Sébastien
// Illustration pour la défaite du joueur.
begin
	writeln('                                                                           ,--. ');
	writeln('                                                                          {    }');
	writeln('                                                                          K,   }');
	writeln('                                                                         /  ~Y` ');
	writeln('                                                                    ,   /   /   ');
	writeln('                                                                   {_"-K.__/    ');
	writeln('                                                                     `/-.__L._  ');
	writeln('                                                                     /  " /`\_} ');
	writeln('                                                                    /  " /      ');
	writeln('                                                            ____   /  " /       ');
	writeln('                                                     ,-"~~~~    ~~/  " /_       ');
	writeln('                                                   ,"             ``~~~  ",     ');
	writeln('                                                  (                        Y    ');
	writeln('                                                 {                         I    ');
	writeln('                                                {      -                    `,  ');
	writeln('                                                |       ",                   )  ');
	writeln('                                                |        |   ,..__      __. Y   ');
	writeln('                                                |    .,_./  Y " / ^Y   J   )|   ');
	writeln('                                                \           |" /   |   |   ||   ');
	writeln('                                                 \          L_/    . _ (_,."(   ');
	writeln('                                                  \,   ,      ^^""" / |      )  ');
	writeln('                                                    \_  \          /,L]     /   ');
	writeln('                                                      "-_~-,       ` `   ./`    ');
	writeln('                                                         `"{_            )      ');
	writeln('                                                             ^^\..___,.--`      ');
end;

procedure victoryScreen();
// RODRIGUEZ Sébastien
// Illustration pour la victoire du joueur.
begin
	writeln('                       *******************************************************************************');
	writeln('                                |                   |                  |                     |        ');
	writeln('                       _________|________________.=""_;=.______________|_____________________|________');
	writeln('                       |                   |  ,-"_,=""     `"=.|                  |                   ');
	writeln('                       |___________________|__"=._o`"-._        `"=.______________|___________________');
	writeln('                                 |                `"=._o`"=._      _`"=._                     |       ');
	writeln('                        _________|_____________________:=._o "=._."_.-="""=.__________________|_______');
	writeln('                       |                   |    __.--" , ; `"=._o." ,-"""-._ ".   |                   ');
	writeln('                       |___________________|_._"  ,. .` ` `` ,  `"-._"-._   ". "__|___________________');
	writeln('                                 |           |o`"=._` , "` `; .". ,  "-._"-._; ;              |       ');
	writeln('                        _________|___________| ;`-.o`"=._; ." ` "`."\` . "-._ /_______________|_______');
	writeln('                       |                   | |o;    `"-.o`"=._``  "` " ,__.--o;   |                   ');
	writeln('                       |___________________|_| ;     (#) `-.o `"=.`_.--"_o.-; ;___|___________________');
	writeln('                       ____/______/______/___|o;._    "      `".o|o_.--"    ;o;____/______/______/____');
	writeln('                       /______/______/______/_"=._o--._        ; | ;        ; ;/______/______/______/_');
	writeln('                       ____/______/______/______/__"=._o--._   ;o|o;     _._;o;____/______/______/____');
	writeln('                       /______/______/______/______/____"=._o._; | ;_.--"o.--"_/______/______/______/_');
	writeln('                       ____/______/______/______/______/_____"=.o|o_.--""___/______/______/______/____');
	writeln('                       /______/______/______/______/______/______/______/______/______/______/______/_');
	writeln('                       *******************************************************************************');
end;

procedure titre ();
// RODRIGUEZ Sébastien
begin
	writeln('             SSSSSSSSSSSSSSS                     hhhhhh                iiii                                          ');
	writeln('           SS:::::::::::::::S                    h::::h               i::::i                                         ');
	writeln('          S:::::SSSSSS::::::S                    h::::h                iiii                                          ');
	writeln('          S:::::S     SSSSSSS                    h::::h                                                              ');
	writeln('          S:::::S            ppppp   ppppppppp    h::::h hhhhh        iiiiiii nnnn  nnnnnnnn     xxxxxxx      xxxxxxx');
	writeln('          S:::::S            p::::ppp:::::::::p   h::::hh:::::hhh     i:::::i n:::nn::::::::nn    x:::::x    x:::::x ');
	writeln('           S::::SSSS         p:::::::::::::::::p  h::::::::::::::hh    i::::i n::::::::::::::nn    x:::::x  x:::::x  ');
	writeln('            SS::::::SSSSS    pp::::::ppppp::::::p h:::::::hhh::::::h   i::::i nn:::::::::::::::n    x:::::xx:::::x   ');
	writeln('              SSS::::::::SS   p:::::p     p:::::p h::::::h   h::::::h  i::::i   n:::::nnnn:::::n     x::::::::::x    ');
	writeln('                 SSSSSS::::S  p:::::p     p:::::p h:::::h     h:::::h  i::::i   n::::n    n::::n      x::::::::x     ');
	writeln('                      S:::::S p:::::p     p:::::p h:::::h     h:::::h  i::::i   n::::n    n::::n      x::::::::x     ');
	writeln('                      S:::::S p:::::p    p::::::p h:::::h     h:::::h  i::::i   n::::n    n::::n     x::::::::::x    ');
	writeln('          SSSSSSS     S:::::S p:::::ppppp:::::::p h:::::h     h:::::h i::::::i  n::::n    n::::n    x:::::xx:::::x   ');
	writeln('          S::::::SSSSSS:::::S p::::::::::::::::p  h:::::h     h:::::h i::::::i  n::::n    n::::n   x:::::x  x:::::x  ');
	writeln('          S:::::::::::::::SS  p::::::::::::::pp   h:::::h     h:::::h i::::::i  n::::n    n::::n  x:::::x    x:::::x ');
	writeln('           SSSSSSSSSSSSSSS    p::::::pppppppp     hhhhhhh     hhhhhhh iiiiiiii  nnnnnn    nnnnnn xxxxxxx      xxxxxxx');
	writeln('                              p:::::p                                                                                ');
	writeln('                              p:::::p                                                                                ');
	writeln('                             p:::::::p                                                                               ');
	writeln('                             p:::::::p                                                                               ');
	writeln('                             p:::::::p                                                                               ');
	writeln('                             ppppppppp                                                                               ');
end;

procedure logo();
// RODRIGUEZ Sébastien
// Illustration pour l'écran titre.
begin
	writeln('                         .sSSSSSSSs                          ___                         sSSSSSSSs.                       ');
	writeln('                         sSS=""^^^"s                       ."///".                       s"^^^""=SSs                      ');
	writeln('              /\       , /  \_\_\|_/_)                    /|<> <>!\                     (_/_|/_/_/  \ ,       /\          ');
	writeln('             /";;     /| \\\/.-. .-./                    /-|  ^  !-\                     \.-. .-.\/// |\     ;;"\         ');
	writeln('            / \;|    /. \,S"  -   - |                   /-- \_=_/ --\                    | -   -  "S,/ .\    |;/ \        ');
	writeln('           / -.;|    | ".SS     _|  ;                   )---| W |---(                    ;  |_     SS." |    |;.- \       ');
	writeln('          ; "-.;\,   |"-.SS\   __  /S                  /-\--| W |--/-\                   S\  __   /SS.-"|   ,/;.-" ;      ');
	writeln('          | _  ";\\.  \" SSS\_____/SS                 (_-_--|_-_|--___)                  SS\_____/SSS "/  .//;"  _ |      ');
	writeln('          |  "- ";\\.  \_SSS[_____]SS                (-___  -_-- _-- -_)                 SS[_____]SSS_/  .//;" -"  |      ');
	writeln('          \ "--.-";;-. __SSS/\    SSS                )-_ _--_ _ ___--__|                 SSS    /\SSS__ .-;;"-.--" /      ');
	writeln('           \  .--" ";;.=SSS`\\_\_SSS                 (___ --__  __ __--(                 SSS_/_//`SSS=.";;" "--.  /       ');
	writeln('            `._ .-"` _";;..=.=.=.\.=\               /-_  / __ -_ -__  \_\                /=./.=.=.=..;;"_ `"-. _.`        ');
	writeln('               ;-._-"  _.;\.=.=.=.|.=|             _>/  -- /|___| _ \ -_ )              |=.|.=.=.=./;._  "-_.-;           ');
	writeln('     ,     _.-"    `"=._  ;\=.=__/__/             /--  _ - _/   \>\ -  -- \              \__\__\=.=/;  _.="`   "-._     , ');
	writeln('     )\ ."`   __        ".;|.=.=.=./             ( / / /   >     \   \ \ \_)              \.=.=.=./;."        __   `". /( ');
	writeln('     (_\   .-`  ".   |    \/=.=.=/"              | |-" | |/       \| |   |_|               `\=.=.=\/    |   ."  `-.   /_) ');
	writeln('      /\\         \-,|     |.--"|                L_|_|_|_/         L_L_|_l_)                  |"--.|    |,-/         //\  ');
	writeln('     /  \`,       //  \    | |  |                                                             |  | |   /  \\       ,`/  \ ');
	writeln('    ( (__) )  _.-"--,  \   | |  "--,                                                       ,--"  | |  /  ,(("-._  ( (__) )');
	writeln('     ;----" -"--,__}}}  \  "--, __}}}                                                     {{{__ ,--" /  {{{__,--"- "----; ');
	writeln('     \_________}}}       \___}}}                                                               {{{___/       {{{________/ ');
end;

//
//                    //    AFFICHAGE DE LA GRILLE        //
//

procedure convertGrille(grille : matrix ; var grilchar : matstring ; taille : coord);
// INGOUFF Christian, 11/01/12 - Version 1.0 ; 17/01/12 - Version 1.7 : caractères spéciaux
// Convertit la grille (array of array of integer) en grilchar (array of array of char) afin d'être affichée à l'utilisateur.
var
	i,j : integer;
begin
	setLength(grilchar,taille[1],taille[2]);
	for i := 0 to taille[1]-1 do
		for j := 0 to taille[2]-1 do
			case grille[i+1,j+1] of
				-42 : grilchar[i,j] := ' ▓▓▓ '; // Mur
				10 : grilchar[i,j] := '  o  '; // Porte d'entrée
				0 : grilchar[i,j] := '     '; // Vide
				-1 : grilchar[i,j] := '  .  '; // Chemin
				15 : grilchar[i,j] := '  ▲  '; // Pions
				16 : grilchar[i,j] := '  ▼  ';
				17 : grilchar[i,j] := '  ◄  ';
				18 : grilchar[i,j] := '  ►  ';
				42 : grilchar[i,j] := '  X  '; // Trésor
			end;
end;

function grilBordHaut(taille : coord):string;
// INGOUFF Christian, 20/01/12 - Version 1.0
// Retourne la bordure supérieure des grilles affichées à l'utilisateur.
var
	i : integer;
begin
	grilBordHaut := '   .';
	for i := 1 to taille[1]-1 do
	begin
		if i < 10 then grilBordHaut := grilBordHaut+'_';
		grilBordHaut := grilBordHaut+'_'+IntToStr(i)+'___';
	end;
	if i < 10 then grilBordHaut := grilBordHaut+'_';
		grilBordHaut := grilBordHaut+'_'+IntToStr(i+1)+'__.     ';
end;    

function grilCaseBord(taille : coord ; haut : boolean):string;
// INGOUFF Christian, 20/01/12 - Version 1.0
// Retourne la bordure supérieure ou inférieure des cases des grilles affichées à l'utilisateur.
var
	i : integer;
begin
	grilCaseBord := '   ';
	if haut then
	for i := 0 to taille[1]-1 do
		grilCaseBord := grilCaseBord+'|     '
	else
	for i := 0 to taille[1]-1 do
		grilCaseBord := grilCaseBord+'|_____';
	grilCaseBord := grilCaseBord+'|     ';
end;

function grilCaseMilieu(grilchar : matstring ; taille : coord ; line : integer ; tourjoueur : boolean):string;
// INGOUFF Christian, 20/01/12 - Version 1.0
// Retourne l'intérieur des cases des grilles affichées à l'utilisateur.
var
	i : integer;
begin
	grilCaseMilieu := ' ';
	if line < 9 then grilCaseMilieu := grilCaseMilieu+' ';
	grilCaseMilieu := grilCaseMilieu+IntToStr(line+1);
	for i := 0 to taille[1]-1 do
		grilCaseMilieu := grilCaseMilieu+'|'+grilchar[i,line];
	grilCaseMilieu := grilCaseMilieu+'|     ';
end;

procedure affichHautGrille(taille : coord ; tourjoueur : boolean);
// INGOUFF Christian, 20/01/12 - Version 1.0
// Retourne ce qui est affiché en haut de la grille.
var
	i : integer;
begin
	if tourjoueur then TextColor(White) else TextColor(Red);
	LowVideo;	
	for i := 1 to ((taille[2] div 2)) do write('    ');
	write(' ----- Grille n°1 -----');
	if tourjoueur then TextColor(Red) else TextColor(White);
	LowVideo;
	for i := 1 to (taille[2]-2) do write('     ');
	writeln('    ----- Grille n°2 -----');
	writeln('');
	if tourjoueur then TextColor(White) else TextColor(Red);
	LowVideo;
	write(grilBordHaut(taille));
	if tourjoueur then TextColor(Red) else TextColor(White);
	LowVideo;
	writeln(grilBordHaut(taille));
end;

procedure affichGrille(grillejeu1, grillejeu2 : matrix ; taille : coord ; tourjoueur : boolean);
// INGOUFF Christian, 11/01/12 - Version 1.0
// Affiche la grille de jeu à l'écran
var
  line : integer;
  grilcharjr, grilcharbot : matstring;
begin
	convertgrille(grillejeu1,grilcharjr,taille);
	convertgrille(grillejeu2,grilcharbot,taille);
	affichHautGrille(taille,tourjoueur);
	for line := 0 to taille[2]-1 do
	begin
		if tourjoueur then TextColor(White) else TextColor(Red);
		LowVideo;	
		write(grilCaseBord(taille,TRUE));
		if tourjoueur then TextColor(Red) else TextColor(White);
		LowVideo;
		writeln(grilCaseBord(taille,TRUE));
		if tourjoueur then TextColor(White) else TextColor(Red);
		LowVideo;	
		write(grilCaseMilieu(grilcharjr,taille,line,tourjoueur));
		if tourjoueur then TextColor(Red) else TextColor(White);
		LowVideo;
		writeln(grilCaseMilieu(grilcharbot,taille,line,tourjoueur));
		if tourjoueur then TextColor(White) else TextColor(Red);
		LowVideo;	
		write(grilCaseBord(taille,FALSE));
		if tourjoueur then TextColor(Red) else TextColor(White);
		LowVideo;
		writeln(grilCaseBord(taille,FALSE));
	end;
	TextColor(White);
	LowVideo;
end;

//
//                                                                 //    VERIFICATION DE LA GRILLE    //
//

procedure arrayDirec(var direc : array_coord ; loc : coord);
// INGOUFF Christian, 15/01/12 - Version 1.0
// Retourne les coordonnées des cases environnantes (dans direc)
// (1,1),(1,2) : haut | (2,1),(2,2) : bas | (3,1),(3,2) : gauche | (4,1),(4,2) : droite
begin
	direc[1,1] := loc[1];	// Haut
	direc[1,2] := loc[2]-1;
  
	direc[2,1] := loc[1];	// Bas
	direc[2,2] := loc[2]+1;
  
	direc[3,1] := loc[1]-1;	// Gauche
	direc[3,2] := loc[2];
  
	direc[4,1] := loc[1]+1; // Droite
	direc[4,2] := loc[2];
end;

procedure extracGrille(var grille : matrix ; grilcoups : matrix ; loc : coord);
// INGOUFF Christian, 16/01/12
// Modélise le chemin obtenu avec algo Dijkstra. Part de trésor (case = nombre de coups) pour aller au départ (case = 0)
var
	direc : array_coord;
	i : integer;
begin
	arrayDirec(direc,loc);
	i := 1;
	if grille[loc[1],loc[2]] <> 10 then
		while i <= 4 do
			if grilcoups[direc[i,1],direc[i,2]] = grilcoups[loc[1],loc[2]] - 1 then
			begin
				grille[loc[1],loc[2]] := -1;
				extracGrille(grille,grilcoups,direc[i]);
				i := 5;
			end
			else
				i := i+1;
end;

procedure bornes(var inf, sup : coord ; enter, taille : coord ; dist : integer);
// INGOUFF Christian, 16/01/12
// Définit inf et sup (bornes inférieure et supérieure) dans le cadre de "dijkstra"
var
	i : integer;
begin
	for i := 1 to 2 do
	begin
		inf[i] := enter[i]-dist+1;
		if inf[i] < 1 then inf[i] := 1;
		sup[i] := enter[i]+dist+1;
		if sup[i] > taille[i] then sup[i] := taille[i];
	end;
end;

procedure dijkstra(grille : matrix ; var grilcoups : matrix ; tresor, enter, taille : coord ; var nb_coups : integer);
// INGOUFF Christian, 16/01/12
// Test algorithme de Dijkstra (Chemin le plus court optimal, mais assez lourd en opérations)
// Retournera la grille tampon (grilcoups) avec les cases libres marquées de leur distance par rapport au départ, ainsi que le nombre de coups optimal.
var
	i, j, k, dist : integer; // dist = Distance par rapport au départ
	loc, inf, sup : coord; // inf et sup = valeurs qui augmenteront selon la distance au départ (recherche en "cercle")
	direc : array_coord; // Cases adjacentes à une case donnée
	valide : boolean; // Sera FAUX si la grille s'avère ne pas être valide.
begin
	dist := 0;
	valide := TRUE;
	while valide and (grilcoups[tresor[1],tresor[2]] = -1) do // Boucle jusqu'à trésor trouvé ou impossible de continuer
	begin
		valide := FALSE; // Initialisation de "valide"
		bornes(inf,sup,enter,taille,dist); // Définition des bornes inférieure et supérieure
		for i := inf[1]-1 to sup[1]-1 do
			for j := inf[2]-1 to sup[2]-1 do // On parcourt le tableau d'abord en cercle (avec inf et sup) puis en entier quand dist > taille
				if grilcoups[i,j] = dist then // Si la case correspond à ce tour (dist)
				begin
					loc[1] := i;
					loc[2] := j; // On assigne les coordonnées de cette case à "loc"
					arrayDirec(direc,loc); // On obtient les coordonnées des cases adjacentes
					for k := 1 to 4 do // Pour chaque case adjacente
						if grilcoups[direc[k,1],direc[k,2]] = -1 then // Si la case n'a pas été franchie
						begin
							grilcoups[direc[k,1],direc[k,2]] := dist+1; // La remplir pour le tour suivant (dist+1)
							valide := TRUE; // Indique qu'au moins une case a été remplie pendant ce tour
						end;
				end;
		dist := dist+1; // On continue tant que le trésor n'a pas été marqué de sa distance par rapport au départ,
				// ou jusqu'à ce que toutes les cases accessibles soient marquées.
	end;
	if not valide then nb_coups := -1 // Grille non valide
	else nb_coups := dist; // Nombre de coups optimal retourné
end;

//
//                  					  //    INITIALISATION DE LA GRILLE    //
//

procedure remplCase(var grille : matrix ; taille : coord ; type_case : integer ; var coord_case : coord);
// INGOUFF Christian, 11/01/12
// Permet d'attribuer à une case au hasard d'une grille une valeur particulière (le type de case : trésor, entrée, murs) si elle est libre.
// Retourne la grille avec cette case remplie et les coordonnées de la case remplie.
var
  valide : boolean;
begin
	repeat
		coord_case[1] := random(taille[1]-1) + 1;
		coord_case[2] := random(taille[1]-1) + 1;
		valide := grille[coord_case[1],coord_case[2]] = 0;
		if valide then
			grille[coord_case[1],coord_case[2]] := type_case;
	until valide;
end;

procedure remiseZero(var grille, grille2 : matrix ; taille : coord ; k : integer);
// INGOUFF Christian, 11/01/12
// Définit les murs aux bords de la zone de jeu valide
// k = -1 dans le cadre de l'initialisation de la grille valide (grille2 va être une grille "tampon" qui va être remplie avec l'algorithme de Dijkstra (voir plus haut))
// k = 0 dans le cadre de l'initialisation des grilles vides (celles qui vont être affichées lors de la phase de jeu)
var
	i, j : integer;
begin
	for i := 0 to taille[1]-1 do
		for j := 0 to taille[2]-1 do
			if (i = 0) or (j = 0) or (i = taille[1]-1) or (j = taille[2]-1) then
			begin
				grille2[i,j] := -42;
				grille[i,j] := -42;
			end
			else
			begin
				grille[i,j] := 0;
				grille2[i,j] := k;
			end;
end;

procedure iniGrille(var grille:matrix; taille : coord ; var tresor, enter : coord ; difficultymin : integer);
// Pré-condition : Reçoit un tableau vide de taille définie (non nulle) et la difficulté du jeu aussi définie
// Post-condition : Renvoie la grille remplie et les coordonnées du trésor et de l'entrée.
// INGOUFF Christian, 11/01/12
var
	i, nb_coups, nb_murs : integer;
	tmp : coord;
	grilcoups : matrix; // Grille qui va gérer le vérificateur de grilles (nombre de coups)
begin
	setLength(grilcoups,taille[1],taille[2]);
	remiseZero(grille,grilcoups,taille,-1); // Initialise les grilles
	nb_murs := trunc(( 15 / 49 ) * ( (taille[1]-2) * (taille[2]-2) )); // Définit le nombre de murs
	remplCase(grille,taille,42,tresor); // Trésor = 42
	remplCase(grille,taille,10,enter); // Porte d'entrée = 10
	grilcoups[enter[1],enter[2]] := 0; // Point d'entrée pour "dijkstra"
	for i := 1 to nb_murs do
	begin
		remplCase(grille,taille,-42,tmp); // Mur = -42
		grilcoups[tmp[1],tmp[2]] := -42; // Reporte les murs sur la grille pour "dijkstra"
	end;
	dijkstra(grille,grilcoups,tresor,enter,taille,nb_coups); // Vérifie si la grille est valide ou non
	if nb_coups < difficultymin then			// Si la grille n'est valide ou ne vérifie pas les conditions de la difficulté de jeu
		iniGrille(grille,taille,tresor,enter,difficultymin)
	else
	begin
		extracGrille(grille,grilcoups,tresor);
		grille[tresor[1],tresor[2]] := 42;
		grille[enter[1],enter[2]] := 10;
		writeln('Nombre de coups optimal : ',nb_coups,#10,#13);
	end;
end;

//
//          				              //    DEPLACEMENT DU JOUEUR ET DU BOT        //
//

procedure deplacement(grille : matrix ; var grillejeu1 : matrix ; var joueur : coord ; enter : coord ; direc : array_coord ; sens : integer ; var valide : boolean);
// RODRIGUEZ Sébastien, INGOUFF Christian - 16/01/12
// Vérifie si une case est libre, si une case correspond à l'entrée et réagit correctement.
// Sens : 1 = haut, 2 = bas, 3 = gauche, 4 = droite (cf. procédure "bouger")
// Valide : Renvoie si le déplacement est valide ou non
begin
	valide := grille[direc[sens,1],direc[sens,2]] > -42;
	if valide then
	begin
		if (joueur[1] = enter[1]) and (joueur[2] = enter[2]) then
			grillejeu1[joueur[1],joueur[2]] := 10
		else
			grillejeu1[joueur[1],joueur[2]] := -1;
		joueur := direc[sens];
		grillejeu1[joueur[1],joueur[2]] := 14+sens;
	end
	else
	begin
		grillejeu1[direc[sens,1],direc[sens,2]] := -42;
		grillejeu1[joueur[1],joueur[2]] := 14+sens;
	end;
end;

procedure victory(gagnant : string);
// RODRIGUEZ Sébastien - 16/01/12
// Affiche l'écran de victoire (du joueur)
begin
	clrscr;
	TextColor(White);
	LowVideo;
	titre();
	writeln('');
	writeln('');
	victoryScreen();
	writeln('');
	writeln('                                    $$$$     BRAVO, ',gagnant,' trouvé le trésor !     $$$$',#10,#13);
	writeln('                                               Appuyez sur ENTREE pour rejouer.');
	writeln('                                Appuyez sur n''importe quelle autre touche pour retourner au menu.');
	writeln('');
	repeat until KeyPressed;
	clrscr;
	
end;

procedure defeat();
// RODRIGUEZ Sébastien - 16/01/12
// Affiche l'écran de défaite (du joueur)
begin
	clrscr;
	TextColor(red);
	LowVideo;
	titre();
	writeln('');
	writeln('');
	defeatScreen();
	writeln('');
	writeln('                                $$$$     Dommage, votre rival a trouvé le trésor avant vous!     $$$$',#10,#13);
	writeln('                                               Appuyez sur ENTREE pour rejouer.');
	writeln('                                Appuyez sur n''importe quelle autre touche pour retourner au menu.');
	writeln('');
	repeat until KeyPressed;
	clrscr;
	TextColor(White);
	LowVideo;
end;

function dijkstraCoup(grillejeu2 : matrix ; taille, enter, xbot, tresor : coord ; direc : array_coord):integer;
// INGOUFF Christian - 16/01/12
// Retourne la case que le bot devrait suivre pour suivre le chemin optimal (suivant l'algorithme de Dijkstra)
var
	i, j : integer;
 	griltmp, griltmp2 : matrix;
begin
	setLength(griltmp,taille[1],taille[2]);
	setLength(griltmp2,taille[1],taille[2]);
	for i := 0 to taille[1]-1 do
		for j := 0 to taille[2]-1 do
			if grillejeu2[i,j] = -42 then griltmp[i,j] := -42
			else griltmp[i,j] := -1;
	griltmp[xbot[1],xbot[2]] := 0;
	dijkstra(grillejeu2,griltmp,tresor,xbot,taille,i);
	extracGrille(griltmp2,griltmp,tresor);
	i := 1;
	repeat
		if griltmp2[direc[i,1],direc[i,2]] = -1 then
		begin
			dijkstraCoup := i;
			i := 5;
		end
		else i := i+1;
	until i > 4;
end;

procedure bot(grille, grillejeu1 : matrix ; var grillejeu2 : matrix ; taille, enter, tresor : coord ; var xbot : coord ; var win : boolean ; var tmpstr : string ; ecran_gauche : boolean);
// INGOUFF Christian - 16/01/12
// Procédure qui gère le déplacement du bot
var
	direc : array_coord;
	ch : char;
	tailletmp : coord;
	valide : boolean;
begin
	tailletmp[1] := taille[1]-2;
	tailletmp[2] := taille[2]-2;
	if ecran_gauche then affichGrille(grillejeu2,grillejeu1,tailletmp,TRUE)
	else affichGrille(grillejeu1,grillejeu2,tailletmp,FALSE);
	writeln('');
	writeln('     ',tmpstr);
	delay(500);
	ch := ' ';
//	repeat ch := ReadKey until (ch = #13) or (ch = #27);
//	if ch = #27 then
//	begin
//		writeln('   Voulez-vous vraiment quitter ? Appuyez sur ECHAP encore une fois pour confirmer ou sur RET. ARRIERE pour continuer.');
//		repeat ch := ReadKey until (ch = #8) or (ch = #27);
//	end;
	arrayDirec(direc,xbot);
	deplacement(grille,grillejeu2,xbot,enter,direc,dijkstraCoup(grillejeu2,taille,enter,xbot,tresor,direc),valide);
	clrscr;
	if not valide then tmpstr := 'Votre rival a rencontré un mur ! C''est à votre tour.' else tmpstr := '';
	win := (xbot[1] = tresor[1]) and (xbot[2] = tresor[2]);
	if ch = #27 then tmpstr := 'QUITTER'
	else if valide then
		bot(grille,grillejeu1,grillejeu2,taille,enter,tresor,xbot,win,tmpstr,ecran_gauche);
end;

procedure debutMenu();
forward;

procedure bouger(grille, grillejeu2 : matrix ; var grillejeu1 : matrix ; taille, enter, tresor : coord ; var joueur : coord ; var win : boolean ; var tmpstr : string ; ecran_gauche : boolean);
// RODRIGUEZ Sébastien - 16/01/12
// Procédure qui gère le déplacement du joueur
var
	direc : array_coord;
	ch : char;
	valide : boolean;
	tailletmp : coord;
begin
	tailletmp[1] := taille[1]-2;
	tailletmp[2] := taille[2]-2;
	if ecran_gauche then affichGrille(grillejeu1,grillejeu2,tailletmp,TRUE)
	else affichGrille(grillejeu2,grillejeu1,tailletmp,FALSE);
	writeln('');
	writeln('     ',tmpstr);
	arrayDirec(direc,joueur); // On obtient les valeurs des cases environnantes
	repeat ch:=ReadKey until (ch = #0) or (ch = #27);
	if ch = #27 then writeln('   Voulez-vous vraiment quitter ? Appuyez sur ECHAP encore une fois pour confirmer ou sur RET. ARRIERE pour annuler.');
	ch:=ReadKey; {Read ScanCode}
	valide := TRUE;
	case ch of
		#72 :   deplacement(grille,grillejeu1,joueur,enter,direc,1,valide); // Haut
		#80 :   deplacement(grille,grillejeu1,joueur,enter,direc,2,valide); // Bas
		#75 :   deplacement(grille,grillejeu1,joueur,enter,direc,3,valide); // Gauche
		#77 :   deplacement(grille,grillejeu1,joueur,enter,direc,4,valide); // Droite
	end;
	clrscr;
	if not valide then tmpstr := 'Vous avez rencontré un mur ! C''est maintenant au tour de votre rival. Appuyez sur ENTREE.' else tmpstr := 'Utilisez les flèches directionelles';
	win := (joueur[1] = tresor[1]) and (joueur[2] = tresor[2]);
	if ch = #27 then tmpstr := 'QUITTER'
	else if valide then
		bouger(grille,grillejeu2,grillejeu1,taille,enter,tresor,joueur,win,tmpstr,ecran_gauche);
end;

//
//                  						  //    MENU PRINCIPAL    //
//

function ecrireFleche(ok : boolean):string;
// RODRIGUEZ Sébastien - 11/01/12
// Retourne une flèche (en ASCII) si le choix dans le menu principal correspond
begin
	if ok then
		ecrireFleche:=(' <--')
	else
		ecrireFleche:=('');   
end;

procedure choixMenu(choix:integer);
// RODRIGUEZ Sébastien - 11/01/12
// Interface graphique/textuelle pour le menu principal
begin
	clrscr;
	titre();
	writeln('                                                      NOUVELLE PARTIE'+ecrireFleche(choix=0));
	writeln('                                                      PARAMETRES'+ecrireFleche(choix=1));
	writeln('                                                      COMMENT JOUER?'+ecrireFleche(choix=2));
	writeln('                                                      QUITTER'+ecrireFleche(choix=3));
end;

function quiCommence():boolean;
// INGOUFF Christian - 20/01/12
// Simulateur de pile ou face, retourne VRAI ou FAUX au hasard
var
	test : integer;
begin
	test := random(2);
	quiCommence := test = 0;
end;

procedure setGrille(var grille1, grille2, grillejeu1, grillejeu2 : matrix ; var taille : coord);
begin
	taille[1] := 11+2;
	taille[2] := 11+2;
	setLength(grille1,taille[1],taille[2]);
	setLength(grille2,taille[1],taille[2]);
	setLength(grillejeu1,taille[1],taille[2]);
	setLength(grillejeu2,taille[1],taille[2]);
end;

procedure quiGagne(turn : boolean ; mode_de_jeu : integer);
begin
	if turn then
		case mode_de_jeu of
			0 :	victory('l''ordinateur 1 a');
			1 :	victory('vous avez');
			2 :	victory('le joueur 1 a');
		end
	else
		case mode_de_jeu of
			0 :	victory('l''ordinateur 2 a');
			1 :	defeat();
			2 :	victory('le joueur 2 a');
		end;
end;

procedure ordonnanceur(grille1, grille2 : matrix ; var grillejeu1, grillejeu2 : matrix ; taille, enter1, enter2, tresor1, tresor2 : coord ; mode_de_jeu : integer);
var
	joueur1, joueur2 : coord;
	win, turn : boolean;
	tmpstr : string;
begin
	joueur1 := enter1;
	joueur2 := enter2;
	clrscr;
	win := FALSE;
	turn := quiCommence();
	if turn then tmpstr := 'Bienvenue à la nouvelle partie ! Vous commencez, utilisez les flèches directionelles.'
	else tmpstr := 'Bienvenue à la nouvelle partie ! Votre rival commence, appuyez sur ENTREE';
	repeat
		if turn and not win then
			case mode_de_jeu of
				0 :	bot(grille1,grillejeu2,grillejeu1,taille,enter1,tresor1,joueur1,win,tmpstr,turn);
				1,2 :	bouger(grille1,grillejeu2,grillejeu1,taille,enter1,tresor1,joueur1,win,tmpstr,turn);
			end
		else if not win then
			case mode_de_jeu of
				0,1 :	bot(grille2,grillejeu1,grillejeu2,taille,enter2,tresor2,joueur2,win,tmpstr,turn);
				2 :	bouger(grille2,grillejeu1,grillejeu2,taille,enter2,tresor2,joueur2,win,tmpstr,turn);
			end;
		turn := not turn;
	until win or (tmpstr = 'QUITTER');
	if win then quiGagne(not turn,mode_de_jeu); // not turn, car turn est inversé après que quelqu'un arrive au trésor !
end;

procedure nouvellePartie();
// INGOUFF Christian - 20/01/12
// Procédure qui gère l'initialisation de la nouvelle partie
var
	difficultymin, mode_de_jeu : integer;
	grille1, grille2, grillejeu1, grillejeu2 : matrix;
	taille, enter1, enter2, tresor1, tresor2 : coord;
begin
	clrscr;
	setGrille(grille1,grille2,grillejeu1,grillejeu2,taille);
	writeln('Niveau de difficulté ? (pour l''instant nb min coups)');
	readln(difficultymin);
	writeln('Mode de jeu ? 0 bot vs bot, 1 joueur vs bot, 2 joueur vs joueur');
	readln(mode_de_jeu);
	iniGrille(grille1,taille,tresor1,enter1,difficultymin);
	iniGrille(grille2,taille,tresor2,enter2,difficultymin);
	remiseZero(grillejeu1,grillejeu2,taille,0); // Initialise les grilles
	grillejeu1[tresor1[1],tresor1[2]] := 42;
	grillejeu2[tresor2[1],tresor2[2]] := 42;
	grillejeu1[enter1[1],enter1[2]] := 15;
	grillejeu2[enter2[1],enter2[2]] := 15;
	ordonnanceur(grille1,grille2,grillejeu1,grillejeu2,taille,enter1,enter2,tresor1,tresor2,mode_de_jeu);
end;

procedure destinationMenu(choix:integer);
// RODRIGUEZ Sébastien - 11/01/12
// Gère les destinations des choix effectués dans le menu principal
var
	ch : char;
begin
	case choix of
		0:	nouvellePartie();{procedure de Nouvelle Partie}
		1:	writeln('procedure des parametres');{Procedure des Parametres}
		2:	begin
				clrscr;
				titre();
				commentJouer();
				writeln('');
				writeln('');
				repeat
					ch := ReadKey;
				until (ch = #27) or (ch = #8);
			end;
	end;
end;

procedure menuPrincipal(var quit : boolean);
// RODRIGUEZ Sébastien - 11/01/12
// Procédure qui gère le menu principal
var
	choix : integer;
	ch: char;
begin
	choix:=0;
	ch := '0';
	quit := FALSE;
	repeat
		choixMenu(choix);
		ch:=ReadKey;
		if ch = #0 then
		begin
			ch:=ReadKey; {Read ScanCode}
			case ch of
				#72 :	begin
						choix := (choix - 1); {monte dans le choix}
						if (choix <0) then
							choix:=3;
					end;
				#80 :	begin
						choix := (choix + 1);  {descend dans le choix}
						if (choix >3) then
							choix:= 0;
					end;
			end;
		end;
	until (ch=#13);{Entrée}
	quit := choix = 3;
	if (ch=#13) and not quit then
		destinationMenu(choix)
	else clrscr;
end;

procedure debutMenu();
// RODRIGUEZ Sébastien - 11/01/12
// Initialise le menu principal.
var
	quit : boolean;
begin
	clrscr;
	writeln(' ');
	writeln(' ');
	menuPrincipal(quit);
	if not quit then debutMenu();
end;

begin
	clrscr;
	randomize;
	titre();
	writeln(' ');
	writeln(' ');
	logo();
	writeln(' ');
	writeln('                                                           Bienvenue !',#10,#13);
	writeln('                                               Appuyez sur n''importe quelle touche.');
	repeat until ReadKey <> #0;
	debutMenu();
end.
