497 lines
14 KiB
C
497 lines
14 KiB
C
#include "game.h"
|
|
|
|
#include <sys/random.h>
|
|
|
|
#include "maze_2.h"
|
|
|
|
#define CHECK_ALLOC(ptr) do { if (ptr == NULL) { fprintf(stderr, "Erreur d'allocation\n"); exit(EXIT_FAILURE); } } while(0)
|
|
|
|
/****************************/
|
|
/*+ Création et libération +*/
|
|
/****************************/
|
|
|
|
// Génération d'un nouveau jeu
|
|
game* create_newgame(const int sh, const int sv, mask *m, const generator f, const objgenerator fo, const int nb_minotaure, const int tressage)
|
|
{
|
|
maze *p_maze;
|
|
if(m != NULL)
|
|
{
|
|
resize_mask(m, sh, sv);
|
|
p_maze = create_proto_maze(m);
|
|
}
|
|
else
|
|
{
|
|
p_maze = create_proto_maze_nomask(sh, sv);
|
|
}
|
|
gen_minotaurs_maze(p_maze, nb_minotaure);
|
|
(*gen_funs[f]) (p_maze);
|
|
(*obj_funs[fo]) (p_maze);
|
|
braid_maze(p_maze, tressage);
|
|
game *p_game = malloc(sizeof(game));
|
|
CHECK_ALLOC(p_game);
|
|
p_game->m = p_maze;
|
|
p_game->score = 0;
|
|
p_game->nbombs = 0;
|
|
p_game->npolys = 0;
|
|
p_game->player_alive = true;
|
|
p_game->player_dir = NORTH;
|
|
p_game->minotaurs_alive = malloc(nb_minotaure * sizeof(bool));
|
|
CHECK_ALLOC(p_game->minotaurs_alive);
|
|
for(int i = 0; i < nb_minotaure; i++)
|
|
{
|
|
p_game->minotaurs_alive[i] = true;
|
|
}
|
|
p_game->minotaurs_dirs = calloc(nb_minotaure, sizeof(cardinal));
|
|
CHECK_ALLOC(p_game->minotaurs_dirs);
|
|
p_game->nb_deadends = count_dead_ends(p_maze);
|
|
p_game->exits = get_exits_maze(p_maze);
|
|
p_game->turns = 0;
|
|
p_game->log = create_history();
|
|
int start = p_maze->player;
|
|
cardinal useless;
|
|
while ((game_try_kill_player(p_game, &useless) != -1 && p_game->m->nb_minotaurs < p_game->m->nb_reachable - 10)
|
|
|| get_object_maze(p_game->m, start) == EXIT || !can_be_used(p_game->m, start))
|
|
{
|
|
//on évite le spawn kill et le spawn win (cntraite du nombre de minotaures car faut pas forcer non plus)
|
|
getrandom(&start, sizeof(start), 0);
|
|
start %= p_game->m->hsize * p_game->m->vsize;
|
|
}
|
|
free_occupied_maze(p_game->m, p_game->m->player);
|
|
p_game->m->player = start;
|
|
make_occupied_maze(p_game->m, start);
|
|
return p_game;
|
|
}
|
|
|
|
void free_game(game *g) {
|
|
free_history(g->log);
|
|
free(g->minotaurs_alive);
|
|
free(g->minotaurs_dirs);
|
|
free_maze(g->m);
|
|
free(g);
|
|
}
|
|
|
|
/***********************************************/
|
|
/*+ Implémentation des attaques de minotaures +*/
|
|
/***********************************************/
|
|
|
|
// ReSharper disable CppDFANullDereference
|
|
|
|
int game_try_kill_player(game *g, cardinal *card) {
|
|
const int cell = g->m->player;
|
|
int neighbour = get_adj_maze(g->m, cell, NORTH);
|
|
if(neighbour != -1) {
|
|
const int mino = has_minotaur_maze(g->m, neighbour);
|
|
if(mino != -1 && g->minotaurs_alive[mino] && !has_wall_maze(g->m, cell, NORTH)) {
|
|
*card = SOUTH;
|
|
g->minotaurs_dirs[mino] = NORTH;
|
|
g->player_alive = false;
|
|
return mino;
|
|
}
|
|
}
|
|
neighbour = get_adj_maze(g->m, cell, EAST);
|
|
if(neighbour != -1) {
|
|
const int mino = has_minotaur_maze(g->m, neighbour);
|
|
if(mino != -1 && g->minotaurs_alive[mino] && !has_wall_maze(g->m, cell, EAST)) {
|
|
*card = WEST;
|
|
g->minotaurs_dirs[mino] = EAST;
|
|
g->player_alive = false;
|
|
return mino;
|
|
}
|
|
}
|
|
neighbour = get_adj_maze(g->m, cell, SOUTH);
|
|
if(neighbour != -1) {
|
|
const int mino = has_minotaur_maze(g->m, neighbour);
|
|
if(mino != -1 && g->minotaurs_alive[mino] && !has_wall_maze(g->m, cell, SOUTH)) {
|
|
*card = NORTH;
|
|
g->player_alive = false;
|
|
g->minotaurs_dirs[mino] = SOUTH;
|
|
return mino;
|
|
}
|
|
}
|
|
neighbour = get_adj_maze(g->m, cell, WEST);
|
|
if(neighbour != -1) {
|
|
const int mino = has_minotaur_maze(g->m, neighbour);
|
|
if(mino != -1 && g->minotaurs_alive[mino] && !has_wall_maze(g->m, cell, WEST)) {
|
|
*card = EAST;
|
|
g->minotaurs_dirs[mino] = WEST;
|
|
g->player_alive = false;
|
|
return mino;
|
|
}
|
|
}
|
|
//on n'a pas trouvé de minotaure
|
|
return -1;
|
|
}
|
|
|
|
// ReSharper restore CppDFANullDereference
|
|
|
|
/***************************/
|
|
/*+ Traitement des objets +*/
|
|
/***************************/
|
|
|
|
void game_consume_object(game *g, const int iter, const object obj) {
|
|
switch (obj) {
|
|
case SMALLT:
|
|
g->score += iter * VALSMALL;
|
|
break;
|
|
case MEDT:
|
|
g->score += iter * VALMED;
|
|
break;
|
|
case LARGET:
|
|
g->score += iter * VALLARGE;
|
|
break;
|
|
case BOMB:
|
|
g->nbombs += iter;
|
|
break;
|
|
case POLY:
|
|
g->npolys += iter;
|
|
break;
|
|
default: //inclue EXIT et NONE
|
|
break;
|
|
}
|
|
}
|
|
|
|
object game_treat_object(game *g) {
|
|
const int cell = g->m->player;
|
|
const object obj = get_object_maze(g->m, cell);
|
|
game_consume_object(g, 1, obj);
|
|
if(obj != EXIT)
|
|
{
|
|
add_object_maze(g->m, cell, NONE);
|
|
}
|
|
return obj;
|
|
}
|
|
|
|
/**********************************************************/
|
|
/*+ Implémentation d'une demande de mouvement du joueur +*/
|
|
/**********************************************************/
|
|
|
|
bool implement_game_move(game *g, const move mv, const strategy strat) {
|
|
if (mv != M_WAIT)
|
|
{
|
|
g->player_dir = (cardinal)mv;
|
|
}
|
|
if (!g->player_alive || !valid_move_maze(g->m, g->m->player, mv))
|
|
{
|
|
return false;
|
|
}
|
|
if (mv != M_WAIT)
|
|
{
|
|
free_occupied_maze(g->m, g->m->player);
|
|
g->m->player = get_adj_maze(g->m, g->m->player, g->player_dir);
|
|
make_occupied_maze(g->m, g->m->player);
|
|
}
|
|
move *mino_move = malloc(g->m->nb_minotaurs * sizeof(move));
|
|
str_funs[strat](g->m, mv, mino_move);
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (mino_move[i] != M_WAIT)
|
|
{
|
|
g->minotaurs_dirs[i] = (cardinal)mino_move[i];
|
|
}
|
|
if (g->minotaurs_alive[i] && valid_move_maze(g->m, g->m->minotaurs[i], mino_move[i]))
|
|
{
|
|
//si le mino veut et peut bouger
|
|
if (mino_move[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], g->minotaurs_dirs[i]);
|
|
make_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mino_move[i] = M_WAIT; //on corrige le mouvement pour l'historique
|
|
}
|
|
}
|
|
const t_type typ = T_MOVE;
|
|
t_move *tmove = malloc(sizeof(t_move));
|
|
CHECK_ALLOC(tmove);
|
|
tmove->obj = game_treat_object(g);
|
|
tmove->playermove = mv;
|
|
tmove->minomoves = mino_move;
|
|
cardinal card = NORTH;
|
|
tmove->killer = game_try_kill_player(g, &card);
|
|
tmove->dirkill = card;
|
|
const turn t = {typ, .tmove = tmove};
|
|
add_entry_history(t, g->log);
|
|
g->turns++;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
/*******************************/
|
|
/*+ Implémentation des bombes +*/
|
|
/*******************************/
|
|
|
|
bool game_bomb_wall(game *g) {
|
|
if (g->nbombs == 0 || !g->player_alive)
|
|
{
|
|
return false;
|
|
}
|
|
const int neighbour = get_adj_maze(g->m, g->m->player, g->player_dir);
|
|
if (!can_be_used(g->m, neighbour) || !has_wall_maze(g->m, g->m->player, g->player_dir))
|
|
{
|
|
return false;
|
|
}
|
|
g->nbombs--;
|
|
g->turns++;
|
|
del_wall_maze(g->m, g->m->player, g->player_dir);
|
|
const t_type typ = T_BOMB;
|
|
t_bomb *tbomb = malloc(sizeof(t_bomb));
|
|
CHECK_ALLOC(tbomb);
|
|
tbomb->bombdir = g->player_dir;
|
|
tbomb->destroyed = true;
|
|
cardinal card = NORTH;
|
|
tbomb->killer = game_try_kill_player(g, &card);
|
|
tbomb->dirkill = card;
|
|
const turn t = {typ, .tbomb = tbomb};
|
|
add_entry_history(t, g->log);
|
|
return true;
|
|
}
|
|
|
|
/*************************************/
|
|
/*+ Implémentation des polys d'algo +*/
|
|
/*************************************/
|
|
|
|
static bool mino_reachable(maze *p_maze, const int mino, const int cell, const int d)
|
|
{
|
|
if (!can_be_used(p_maze, cell))
|
|
{
|
|
return false;
|
|
}
|
|
if (mino == cell)
|
|
{
|
|
return true;
|
|
}
|
|
if (d == 0)
|
|
{
|
|
return false;
|
|
}
|
|
int c = get_adj_maze(p_maze, cell, NORTH);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, EAST);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, SOUTH);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, SOUTH);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, WEST);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, WEST);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, c, NORTH);
|
|
if (mino_reachable(p_maze, mino, c, d - 1))
|
|
{
|
|
return true;
|
|
}
|
|
c = get_adj_maze(p_maze, cell, NORTH);
|
|
return mino_reachable(p_maze, mino, c, d - 1);
|
|
}
|
|
|
|
bool game_kill_minotaurs(game *g, const int d) {
|
|
if (g->npolys == 0 || !g->player_alive)
|
|
{
|
|
return false;
|
|
}
|
|
const int cell = g->m->player;
|
|
bool done = false;
|
|
bool *minokilled = calloc(g->m->nb_minotaurs, sizeof(bool));
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (g->minotaurs_alive[i] && mino_reachable(g->m, g->m->minotaurs[i], cell, d))
|
|
{
|
|
g->minotaurs_alive[i] = false;
|
|
free_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
done = true;
|
|
minokilled[i] = true;
|
|
}
|
|
}
|
|
if (!done)
|
|
{
|
|
free(minokilled);
|
|
return false;
|
|
}
|
|
g->npolys--;
|
|
g->turns++;
|
|
const t_type typ = T_POLY;
|
|
const turn t = {typ, .minokilled = minokilled};
|
|
add_entry_history(t, g->log);
|
|
return true;
|
|
}
|
|
|
|
|
|
/*****************************/
|
|
/*+ Gestion de l'historique +*/
|
|
/*****************************/
|
|
|
|
bool game_undo(game *g) {
|
|
const turn *t = last_move_history(g->log);
|
|
if (t == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (t->type == T_POLY)
|
|
{
|
|
g->npolys++;
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (t->minokilled[i])
|
|
{
|
|
g->minotaurs_alive[i] = true;
|
|
make_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
}
|
|
}
|
|
g->turns--;
|
|
rewind_history(g->log); //on note que le tour a été annulé
|
|
return true;
|
|
}
|
|
if (t->type == T_BOMB)
|
|
{
|
|
if (t->tbomb->destroyed)
|
|
{
|
|
build_wall_maze(g->m, g->m->player, t->tbomb->bombdir);
|
|
g->nbombs++;
|
|
}
|
|
if (t->tbomb->killer != -1)
|
|
{
|
|
g->player_alive = true;
|
|
make_occupied_maze(g->m, g->m->player);
|
|
}
|
|
g->turns--;
|
|
rewind_history(g->log); //on note que le tour a été annulé
|
|
return true;
|
|
}
|
|
if (t->type == T_MOVE)
|
|
{
|
|
if (t->tmove->obj != NONE)
|
|
{
|
|
add_object_maze(g->m, g->m->player, t->tmove->obj);
|
|
game_consume_object(g, -1, t->tmove->obj);
|
|
}
|
|
if (t->tmove->killer != -1)
|
|
{
|
|
g->player_alive = true;
|
|
}
|
|
if (t->tmove->playermove != M_WAIT)
|
|
{
|
|
free_occupied_maze(g->m, g->m->player);
|
|
g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)((t->tmove->playermove + 2) % 4));
|
|
make_occupied_maze(g->m, g->m->player);
|
|
}
|
|
g->minotaurs_dirs = (cardinal*)t->tmove->minomoves;
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (t->tmove->minomoves[i] != M_WAIT)
|
|
{
|
|
g->minotaurs_dirs[i] = (cardinal)t->tmove->minomoves[i]; //on met à jour les directions des minotaures
|
|
if (g->minotaurs_alive[i])
|
|
{
|
|
free_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)((g->minotaurs_dirs[i] + 2) % 4));
|
|
make_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
}
|
|
}
|
|
}
|
|
g->turns--;
|
|
rewind_history(g->log); //on note que le tour a été annulé
|
|
return true;
|
|
}
|
|
fprintf(stderr, "Erreur d'historique, type inconnu\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
bool game_redo(game *g) {
|
|
const turn *t = next_move_history(g->log);
|
|
if (t == NULL)
|
|
{
|
|
return false;
|
|
}
|
|
if (t->type == T_POLY)
|
|
{
|
|
g->npolys--;
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (t->minokilled[i])
|
|
{
|
|
g->minotaurs_alive[i] = false;
|
|
free_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
}
|
|
}
|
|
g->turns++;
|
|
continue_history(g->log); //on note que le tour a été rejoué
|
|
return true;
|
|
}
|
|
if (t->type == T_BOMB)
|
|
{
|
|
if (t->tbomb->destroyed)
|
|
{
|
|
del_wall_maze(g->m, g->m->player, t->tbomb->bombdir);
|
|
g->player_dir = t->tbomb->bombdir;
|
|
g->nbombs--;
|
|
}
|
|
if (t->tbomb->killer != -1)
|
|
{
|
|
g->minotaurs_dirs[t->tbomb->killer] = t->tbomb->dirkill;
|
|
g->player_alive = false;
|
|
free_occupied_maze(g->m, g->m->player);
|
|
}
|
|
g->turns++;
|
|
continue_history(g->log); //on note que le tour a été rejoué
|
|
return true;
|
|
}
|
|
if (t->type == T_MOVE)
|
|
{
|
|
if (t->tmove->obj != NONE)
|
|
{
|
|
game_treat_object(g);
|
|
}
|
|
if (t->tmove->killer != -1)
|
|
{
|
|
g->player_alive = false;
|
|
}
|
|
if (t->tmove->playermove != M_WAIT)
|
|
{
|
|
free_occupied_maze(g->m, g->m->player);
|
|
g->m->player = get_adj_maze(g->m, g->m->player, (cardinal)t->tmove->playermove);
|
|
make_occupied_maze(g->m, g->m->player);
|
|
}
|
|
for (int i = 0; i < g->m->nb_minotaurs; i++)
|
|
{
|
|
if (t->tmove->minomoves[i] != M_WAIT)
|
|
{
|
|
g->minotaurs_dirs[i] = (cardinal)t->tmove->minomoves[i]; //on met à jour les directions des minotaures
|
|
if (g->minotaurs_alive[i])
|
|
{
|
|
free_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
g->m->minotaurs[i] = get_adj_maze(g->m, g->m->minotaurs[i], (cardinal)t->tmove->minomoves[i]);
|
|
make_occupied_maze(g->m, g->m->minotaurs[i]);
|
|
}
|
|
}
|
|
}
|
|
g->turns++;
|
|
continue_history(g->log); //on note que le tour a été rejoué
|
|
return true;
|
|
}
|
|
fprintf(stderr, "Erreur d'historique, type inconnu\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|