#include "game.h" #include "game_history.h" #include "maze.h" /****************************/ /*+ Création et libération +*/ /****************************/ // Génération d'un nouveau jeu game* create_newgame(int hsize, int vsize, mask* themask, generator gen_maze, objgenerator gen_obj, int nb_min, int braid) { // Allocation mémoire pour le jeu game* g = malloc(sizeof(game)); if (g == NULL) { ERROR("Erreur d'allocation mémoire pour le jeu."); } // Création du proto-labyrinthe maze* lab = NULL; // Avec masque if (themask != NULL) { if (themask->hsize != hsize || themask->vsize != vsize) { // On verifie la taille du masque resize_mask(themask, hsize, vsize); } lab = create_proto_maze(themask); } // Sans masque else { lab = create_proto_maze_nomask(hsize, vsize); } // On verifie la création du labyrinthe if (lab == NULL) { ERROR("Erreur de création du proto-labyrinthe"); } // Modification du labyrinthe en fonction du générateur if (gen_maze < 0 || gen_maze >= GEN_SIZE) { free_game(g); ERROR("Générateur de labyrinthe invalide"); } else { gen_funs[gen_maze](lab); } // Modification du labyrinthe en fonction du générateur d'objets if (gen_obj < 0 || gen_obj >= OBJ_SIZE) { free_game(g); ERROR("Générateur d'objets invalide"); } else { obj_funs[gen_obj](lab); } // Modification du labyrinthe en fonction du nombre de minotaures if (nb_min < 0) { free_game(g); ERROR("Nombre de minotaures invalide"); } else { gen_minotaurs_maze(lab, nb_min); } // Modification du labyrinthe en fonction du tressage if (braid < 0 || braid > 100) { free_game(g); ERROR("Taux de tressage invalide"); } else { braid_maze(lab, braid); } // Initialisation du jeu g->m = lab; // On affecte le labyrinthe g->score = 0; g->nbombs = 0; g->npolys = 0; g->player_alive = true; g->player_dir = rand() % 4; // Le joueur est tourné dans une direction aléatoire g->minotaurs_alive = malloc(sizeof(bool) * g->m->nb_minotaurs); g->minotaurs_dirs = malloc(sizeof(cardinal) * g->m->nb_minotaurs); for (int i = 0; i < g->m->nb_minotaurs; i++) { g->minotaurs_alive[i] = true; // Les minotaures sont tous vivants g->minotaurs_dirs[i] = rand() % 4; // Les minotaures sont tournés dans une direction aléatoire } g->nb_deadends = count_dead_ends(g->m); g->exits = get_exits_maze(g->m); g->turns = 0; g->log = create_history(); if (g->log == NULL) { free_game(g); ERROR("Erreur de création de l'historique"); } if (g == NULL) { ERROR("Le labyrinthe n'a pas été créé"); } return g; } void free_game(game* g) { if (g == NULL) { ERROR("Pointeur null"); } else { if (g->m != NULL) { free_maze(g->m); } if (g->minotaurs_alive != NULL) { free(g->minotaurs_alive); } if (g->minotaurs_dirs != NULL) { free(g->minotaurs_dirs); } if (g->log != NULL) { free_history(g->log); } free(g); } return; } /***********************************************/ /*+ Implémentation des attaques de minotaures +*/ /***********************************************/ int game_try_kill_player(game* g, cardinal* dir) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } // On teste les cases adjacentes au joueur pour les 4 directions for (int i = 0; i < 4; i++) { int adj = get_adj_maze(g->m, g->m->player, i); if (adj != -1 && !has_wall_maze(g->m, g->m->player, i) && has_minotaur_maze(g->m, adj) != -1 && g->minotaurs_alive[has_minotaur_maze(g->m, adj)]) { *dir = (i + 2) % 4; g->player_alive = false; return has_minotaur_maze(g->m, adj); } } // Si on n'a pas trouvé de minotaure adjacent return -1; } /***************************/ /*+ Traitement des objets +*/ /***************************/ void game_consume_object(game* g, int nb_copie, object obj) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } // On teste le type de l'objet switch (obj) { case SMALLT: g->score += nb_copie * VALSMALL; break; case MEDT: g->score += nb_copie * VALMED; break; case LARGET: g->score += nb_copie * VALLARGE; break; case BOMB: g->nbombs += nb_copie; break; case POLY: g->npolys += nb_copie; break; default: break; } if (obj != EXIT && obj != NONE) { for (int i = 0; i < nb_copie; i++) { g->m->objects[g->m->player] = NONE; } } return; } object game_treat_object(game* g) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } // On récupère l'objet de la cellule du joueur object obj = get_object_maze(g->m, g->m->player); // On teste le type de l'objet switch (obj) { case SMALLT: game_consume_object(g, 1, SMALLT); return SMALLT; case MEDT: game_consume_object(g, 1, MEDT); return MEDT; case LARGET: game_consume_object(g, 1, LARGET); return LARGET; case BOMB: game_consume_object(g, 1, BOMB); return BOMB; case POLY: game_consume_object(g, 1, POLY); return POLY; case EXIT: return EXIT; default: break; } return NONE; } /**********************************************************/ /*+ Implémentation d'une demande de mouvement du joueur +*/ /**********************************************************/ bool implement_game_move(game* g, move mov, strategy strag) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } // On teste si le joueur est vivant if (!g->player_alive) { return false; } // On teste si le mouvement est valide if (!valid_move_maze(g->m, g->m->player, mov)) { g->player_dir = (cardinal)mov; return false; } // SINON on fait le mouvement // On déplace le joueur et on le tourne dans la direction dans laquelle il avance free_occupied_maze(g->m, g->m->player); g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)mov); g->player_dir = (cardinal)mov; make_occupied_maze(g->m, g->m->player); // On gère les minotaures // On creer le tableau des mouvements des minotaures // pas la peine de l'initaliser à M_WAIT car on va le remplir move* min_move = malloc(sizeof(move) * g->m->nb_minotaurs); // On applique la stratégie des minotaures str_funs[strag](g->m, mov, min_move); // On teste si les minautores peuvent se déplacer for (int i = 0; i < g->m->nb_minotaurs; i++) { if (g->minotaurs_alive[i]) { // Si le minotaure est vivant et qu'on peut le déplacer dans la direction demandée if (valid_move_maze(g->m, g->m->minotaurs[i], min_move[i])) { free_occupied_maze(g->m, g->m->minotaurs[i]); g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)min_move[i]); make_occupied_maze(g->m, g->m->minotaurs[i]); g->minotaurs_dirs[i] = (cardinal)min_move[i]; } // Sinon on le tourne dans la direction demandée et on mets son mouvements à M_WAIT else { g->minotaurs_dirs[i] = (cardinal)min_move[i]; min_move[i] = M_WAIT; } } } // On teste les objets object obj = game_treat_object(g); // On teste si le joueur est attaqué cardinal* dir_player = malloc(sizeof(cardinal)); *dir_player = g->player_dir; int min_killer = game_try_kill_player(g, dir_player); if (min_killer != -1) { g->player_alive = false; } // On gère l'historique t_move* history_move = malloc(sizeof(t_move)); history_move->obj = obj; history_move->playermove = mov; history_move->minomoves = min_move; history_move->killer = min_killer; // On trouve la direction du minotaure qui a tué le joueur if (min_killer != -1) { for (cardinal i = 0; i < 4; i++) { if (get_adj_maze(g->m, g->m->minotaurs[min_killer], i) == g->m->player) { history_move->dirkill = i; } } } else { history_move->dirkill = NORTH; } turn new_turn = {T_MOVE, .tmove = history_move}; add_entry_history(new_turn, g->log); g->turns++; return true; } /*******************************/ /*+ Implémentation des bombes +*/ /*******************************/ bool game_bomb_wall(game* g) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } // On teste si le joueur a une bombe if (g->nbombs > 0 && g->player_alive) { // On teste si on peut détruire le mur if (get_adj_maze(g->m, g->m->player, g->player_dir) != -1 && valid_maze(g->m, get_adj_maze(g->m, g->m->player, g->player_dir)) && has_wall_maze(g->m, g->m->player, g->player_dir)) { del_wall_maze(g->m, g->m->player, g->player_dir); // On teste si le joueur est attaqué cardinal* dir_player = malloc(sizeof(cardinal)); *dir_player = g->player_dir; int min_killer = game_try_kill_player(g, dir_player); if (min_killer != -1) { g->player_alive = false; } g->nbombs--; // On gère l'historique t_bomb* history_move = malloc(sizeof(t_bomb)); history_move->bombdir = g->player_dir; history_move->destroyed = true; history_move->killer = game_try_kill_player(g, dir_player); // On trouve la direction du minotaure qui a tué le joueur if (min_killer != -1) { for (cardinal i = 0; i < 4; i++) { if (get_adj_maze(g->m, g->m->minotaurs[min_killer], i) == g->m->player) { history_move->dirkill = i; } } } else { // Sinon on met une direction par défaut history_move->dirkill = NORTH; } turn new_turn = {T_BOMB, .tbomb = history_move}; // On ajoute le tour à l'historique add_entry_history(new_turn, g->log); g->turns++; return true; } // Sinon on ne peut pas détruire de mur mais on perd quand même une bombe else { g->nbombs--; // On gère l'historique t_bomb* history_move = malloc(sizeof(t_bomb)); history_move->bombdir = g->player_dir; history_move->destroyed = false; history_move->killer = -1; history_move->dirkill = NORTH; // On trouve la direction du minotaure qui a tué le joueur turn new_turn = {T_BOMB, .tbomb = history_move}; add_entry_history(new_turn, g->log); g->turns++; return true; } } return false; } /*************************************/ /*+ Implémentation des polys d'algo +*/ /*************************************/ bool game_kill_minotaurs(game* g, int portee) { if (g == NULL || g->m == NULL) { ERROR("Pointeur de jeu null"); } if (portee < 0) { ERROR("Portée invalide"); } // On teste si le joueur a un poly d'algo if (g->npolys > 0 && g->player_alive) { int nb_min_kill = 0; // On initialise le tableau des minotaures tués pour l'historique bool* min_killed = malloc(sizeof(bool) * g->m->nb_minotaurs); for (int i = 0; i < g->m->nb_minotaurs; i++) { min_killed[i] = false; } // On teste dans les quatres directions for (int i = 0; i < 4; i++) { int cell_dir = g->m->player; int portee_dir = portee; while (portee_dir > 0 && get_adj_maze(g->m, cell_dir, i) != -1) { int num_mino = has_minotaur_maze(g->m, get_adj_maze(g->m, cell_dir, i)); if (num_mino != -1 && g->minotaurs_alive[num_mino]) { nb_min_kill++; g->minotaurs_alive[num_mino] = false; min_killed[num_mino] = true; free_occupied_maze(g->m, get_adj_maze(g->m, cell_dir, i)); } portee_dir--; cell_dir = get_adj_maze(g->m, cell_dir, i); } } if (nb_min_kill > 0) { g->npolys--; // On gère l'historique turn new_turn = {T_POLY, .minokilled = min_killed}; add_entry_history(new_turn, g->log); g->turns++; return true; } } return false; } /*****************************/ /*+ Gestion de l'historique +*/ /*****************************/ bool game_undo(game* g) { if (g == NULL || g->log == NULL) { ERROR("Pointeur de jeu null"); } // On teste si l'historique est vide if (sizeprev_history(g->log) == 0) { return false; } // Sinon on annule le dernier tour // On récupère le dernier tour turn* last_turn = last_move_history(g->log); rewind_history(g->log); g->turns--; switch (last_turn->type) { case T_MOVE: // On récupère les caractéristiques du tour t_move* history_move = last_turn->tmove; // On teste si le joueur a été attaqué if (history_move->killer != -1) { g->player_alive = true; } // On replace l'objet consommé if (history_move->obj != NONE) { game_consume_object(g, -1, history_move->obj); g->m->objects[g->m->player] = history_move->obj; } // On deplace le joueur free_occupied_maze(g->m, g->m->player); g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)(history_move->playermove + 2) % 4); g->player_dir = (cardinal)((history_move->playermove + 2) % 4); make_occupied_maze(g->m, g->m->player); // On gère les déplacements des minotaures for (int i = 0; i < g->m->nb_minotaurs; i++) { if (g->minotaurs_alive[i]) { // Si le minotaure est vivant on annule son déplacement if (history_move->minomoves[i] != M_WAIT) { free_occupied_maze(g->m, g->m->minotaurs[i]); g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)(history_move->minomoves[i] + 2) % 4); make_occupied_maze(g->m, g->m->minotaurs[i]); g->minotaurs_dirs[i] = (cardinal)(history_move->minomoves[i] + 2) % 4; } } } return true; case T_BOMB: t_bomb* history_bomb = last_turn->tbomb; // Si un mur a été détruit if (history_bomb->destroyed) { // On teste si le joueur a été attaqué if (history_bomb->killer != -1) { g->player_alive = true; } // On replace le mur détruit build_wall_maze(g->m, g->m->player, history_bomb->bombdir); } // On recupère la bombe consommée g->nbombs++; return true; case T_POLY: bool* history_poly = last_turn->minokilled; // On ressucite les minotaures tués for (int i = 0; i < g->m->nb_minotaurs; i++) { if (history_poly[i]) { g->minotaurs_alive[i] = true; make_occupied_maze(g->m, g->m->minotaurs[i]); } } return true; } return false; } bool game_redo(game* g) { if (g == NULL || g->log == NULL) { ERROR("Pointeur de jeu null"); } // On teste si l'historique est vide if (sizenext_history(g->log) == 0) { return false; } // Sinon on remet le dernier tour annulé // On récupère le dernier tour annulé turn* next_turn = next_move_history(g->log); continue_history(g->log); g->turns++; switch (next_turn->type) { case T_MOVE: // On récupère les caractéristiques du tour t_move* history_move = next_turn->tmove; // On teste si le joueur avait été attaqué if (history_move->killer != -1) { g->player_alive = false; } // On deplace le joueur free_occupied_maze(g->m, g->m->player); g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)(history_move->playermove)); g->player_dir = (cardinal)((history_move->playermove)); make_occupied_maze(g->m, g->m->player); // On re-recupère l'objet if (history_move->obj != NONE) { game_consume_object(g, 1, history_move->obj); g->m->objects[g->m->player] = NONE; } // On gère les déplacements des minotaures for (int i = 0; i < g->m->nb_minotaurs; i++) { if (g->minotaurs_alive[i]) { // Si le minotaure est vivant on refait son déplacement if (history_move->minomoves[i] != M_WAIT) { free_occupied_maze(g->m, g->m->minotaurs[i]); g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)(history_move->minomoves[i])); make_occupied_maze(g->m, g->m->minotaurs[i]); g->minotaurs_dirs[i] = (cardinal)(history_move->minomoves[i]); } } } return true; case T_BOMB: t_bomb* history_bomb = next_turn->tbomb; // Si un mur avait été détruit puis reconstruit if (history_bomb->destroyed) { // On teste si le joueur a été attaqué if (history_bomb->killer != -1) { g->player_alive = false; } // On reconstruit le mur del_wall_maze(g->m, g->m->player, history_bomb->bombdir); } return true; case T_POLY: bool* history_poly = next_turn->minokilled; // On tue les minotaures qui avaient été ressucités for (int i = 0; i < g->m->nb_minotaurs; i++) { if (history_poly[i]) { g->minotaurs_alive[i] = false; free_occupied_maze(g->m, g->m->minotaurs[i]); } } return true; } return false; return false; }