APL/APL1.1/SAE11_2021/taquin.c
2021-11-30 09:38:39 +01:00

279 lines
7.2 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <graph.h>
#include <string.h>
#include <time.h>
#include "utils.h"
#include "taquin.h"
#include "graph_sup.h"
#define OFFSET_X 650
#define OFFSET_Y 70
//Variables résponsables de la bonne division en cases et de l'apparence du Taquin.
int ** Taquin;
int Rows, Columns;
char TaquinFilename[500];
//Variables résponsable du bon positionement et de la bonne échelle de l'image.
int ImageX, ImageY, SizeX, SizeY;
//Met à jour l'entièreté du Taquin.
void UpdateTaquin() {
for (int Y = 0; Y < Rows; Y++) {
for (int X = 0; X < Columns; X++) {
UpdatePiece(X, Y, Taquin[X][Y], GetColorN("black"));
}
}
}
//Vérifie si le Taquin est en ordre et retourne 1 si oui, 0 si non.
int CheckVictory() {
int X, Y;
for (int i = 0; i < Rows * Columns; i++) {
X = i % Columns;
Y = i / Columns;
if (Taquin[X][Y] != i) return 0;
}
return 1;
}
//Met à jour une pièce spécifique du Taquin.
void UpdatePiece(int X, int Y, int Index, couleur Color) {
int StartX = OFFSET_X + (X * SizeX);
int StartY = OFFSET_Y + (Y * SizeY) + (500 - ImageY) / 2;
int IStartX = SizeX * (Index % Columns);
int IStartY = SizeY * (Index / Columns);
SetColorC(Color);
if (Index != 0) {
DessinerRectangle(StartX, StartY, SizeX-1, SizeY-1);
ChargerImage(TaquinFilename, StartX+1, StartY+1, IStartX, IStartY, SizeX-2, SizeY-2);
} else {
DessinerRectangle(StartX, StartY, SizeX-1, SizeY-1);
SetColorN("white");
RemplirRectangle(StartX + 1, StartY + 1, SizeX - 2, SizeY - 2);
}
}
//Vérifie la validité d'un mouvement de pièce et le fait s'il est valide.
//Renvoie si le mouvement est valide ou non ( 1 ou 0 )
int MovePiece(int X, int Y, int Index, int ShouldUpdate) {
int NewX = 0;
int NewY = 0;
if (X - 1 >= 0 && Taquin[X-1][Y] == 0) {
NewX = -1;
} else if (X + 1 < Columns && Taquin[X+1][Y] == 0) {
NewX = 1;
} else if (Y - 1 >= 0 && Taquin[X][Y-1] == 0) {
NewY = -1;
} else if (Y + 1 < Rows && Taquin[X][Y+1] == 0) {
NewY = 1;
} else return 0;
Taquin[X][Y] = 0;
Taquin[X + NewX][Y + NewY] = Index;
for (int i = 0; i < BT_Count; i++) {
if (Buttons[i].id == Index) Buttons[i].id = 0;
else if (Buttons[i].id == 0) Buttons[i].id = Index;
}
if (ShouldUpdate) {
UpdatePiece(X + NewX, Y + NewY, Index, GetColorN("black"));
UpdatePiece(X, Y, 0, GetColorN("red"));
}
return NewX + 2 * NewY;
}
//Randomise le Taquin.
void RandomizeTaquin() {
int CX = 0, CY = 0;
int LastMove = -1;
srand(Microsecondes());
for (int i = 0; i < 50000; i++) {
LastMove = rand() % 4 == LastMove ? (LastMove + 1) % 4 : rand() % 4;
srand(Microsecondes());
if (LastMove == 0 && CX + 1 < Columns) /*Droite*/ {
CX++;
MovePiece(CX, CY, Taquin[CX][CY], 0);
} else if (LastMove == 1 && CY + 1 < Rows) /*Bas*/ {
CY++;
MovePiece(CX, CY, Taquin[CX][CY], 0);
} else if (LastMove == 2 && CX - 1 > -1) /*Gauche*/ {
CX--;
MovePiece(CX, CY, Taquin[CX][CY], 0);
} else if (LastMove == 3 && CY - 1 > -1) /*Haut*/ {
CY--;
MovePiece(CX, CY, Taquin[CX][CY], 0);
}
}
}
void TaquinRenderLogicLoop() {
int CurID = -1, LastID = -1, LastX = 0, LastY = 0, Victory = 0;
//Variable indiquant quel périphérique controle actuellement le Taquin
//0 = Souris, 1 = Clavier
int Controller = 0;
//On lance la boucle principale qui servira de boucle graphique et logique.
//Elle met à jour le Taquin lorsqu'une pièce est bougée et déclenche les évenements logiques tels qu'un mouvement de pièce lors d'un clic.
while(!Victory) {
if (DrawNextFrame()) {
if (Controller == 0) {
SourisPosition();
CurID = GetButton(_X, _Y);
}
//Vérifie que la souris clique sur une pièce du Taquin et appelle la fonction de mouvement en fonction.
if (SourisCliquee() && CurID != -1) {
int handled = 0;
Controller = 0;
CurID = GetButton(_X, _Y);
for (int Y = 0; Y < Rows; Y++) {
for (int X = 0; X < Columns; X++) {
if (handled) break;
if (Taquin[X][Y] == CurID) {
int movement = MovePiece(X, Y, CurID, 1);
if (movement % 2 != 0) LastX += SizeX * movement;
else LastY += SizeY * (movement/2);
Victory = CheckVictory();
handled = 1;
break;
}
}
}
}
if (ToucheEnAttente()) {
int key = Touche();
if (CurID == -1) {
CurID = 0;
LastID = -1;
}
int DeltaX = 0, DeltaY = 0;
if (key == XK_Left) {
DeltaX--;
} else if (key == XK_Right) {
DeltaX++;
} else if (key == XK_Up) {
DeltaY--;
} else if (key == XK_Down) {
DeltaY++;
} else if (key == XK_space) {
int handled = 0;
for (int Y = 0; Y < Rows; Y++) {
for (int X = 0; X < Columns; X++) {
if (handled) break;
if (Taquin[X][Y] == CurID) {
int movement = MovePiece(X, Y, CurID, 1);
if (movement % 2 != 0) LastX += SizeX * movement;
else LastY += SizeY * (movement/2);
if (movement != 0) {
LastID = CurID;
CurID = 0;
}
Victory = CheckVictory();
handled = 1;
break;
}
}
}
}
if (DeltaX != 0 || DeltaY != 0) {
Controller = 1;
int handled = 0;
for (int Y = 0; Y < Rows; Y++) {
for (int X = 0; X < Columns; X++) {
if (handled) break;
if (Taquin[X][Y] == CurID) {
LastID = CurID;
CurID = Taquin[clamp(X + DeltaX, 0, Columns-1)][clamp(Y + DeltaY, 0, Rows-1)];
handled = 1;
break;
}
}
}
}
}
//Indicateur lumineux de la case actuellement sélectionnée.
if (CurID != LastID) {
for (int Y = 0; Y < Rows; Y++) {
for (int X = 0; X < Columns; X++) {
if (CurID == Taquin[X][Y]) {
if (CurID != -1) UpdatePiece(X, Y, CurID, GetColorN("red"));
if (LastID!= -1) UpdatePiece(LastX, LastY, LastID, GetColorN("black"));
LastID = CurID;
LastX = X;
LastY = Y;
}
}
}
}
}
}
}
void ShowVictoryScreen() {
}
//Initialise le Taquin, la fonction appelle la création graphique et logique du Taquin.
void CreateTaquin(char * FileName, int ImX, int ImY, int RowNumber, int ColumnNumber) {
Rows = RowNumber;
Columns = ColumnNumber;
ImageX = ImX;
ImageY = ImY;
SizeX = ImageX / Columns;
SizeY = ImageY / Rows;
strcpy(TaquinFilename, FileName);
//On alloue dynamiquement la mémoire afin de stocker l'arrangement des pièces du Taquin.
Taquin = calloc(Columns, sizeof(int*));
for (int i = 0; i < Columns; i++) {
Taquin[i] = calloc(Rows, sizeof(int));
}
//On définit la position de départ de chaque pièce (au début en ordre, puis on randomise)
for (int i = 0; i < Columns * Rows; i++) {
Taquin[i % Columns][i / Columns] = i;
int StartX = OFFSET_X + SizeX * (i % Columns);
int StartY = OFFSET_Y + SizeY * (i / Columns) + (500 - ImageY) / 2;
AddButton(StartX, StartY, SizeX, SizeY, i);
}
//On affiche la solution à gauche
SetColorN("black");
DessinerRectangle(49, OFFSET_Y + (500 - ImageY) / 2 - 1, ImageX + 1, ImageY + 1);
ChargerImage(FileName, 50, OFFSET_Y + (500 - ImageY) / 2, 0, 0, ImageX, ImageY);
RandomizeTaquin(); //On randomise notre taquin.
UpdateTaquin();
TaquinRenderLogicLoop(); //Cette fonction est responsable de tout le déroulement de la partie.
ShowVictoryScreen(); //On est après une partie, on affiche l'écran de victoire.
}