Files
Sokoban/function.c

396 lines
10 KiB
C

#include "function.h"
#include "display.h"
#include <SDL2/SDL_events.h>
#include <SDL2/SDL_keycode.h>
#include <SDL2/SDL_render.h>
#include <SDL2/SDL_scancode.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
unsigned short int **creatArea2D (const unsigned int N)
{
unsigned short int **tab2d;
tab2d = calloc (N, sizeof (unsigned short int *));
if (tab2d == NULL)
{
return NULL;
}
bool fail = false;
unsigned int i;
for (i = 0; i < N && !fail; ++i)
{
tab2d[i] = calloc (N, sizeof (unsigned short int));
if (tab2d[i] == NULL)
{
fail = true;
}
}
if (fail)
{
unsigned int j;
for (j = 0; j < i; ++j)
{
free (tab2d[j]);
}
free (tab2d);
return NULL;
}
return tab2d;
}
void free2D (unsigned short int **tab, int N)
{
int i;
for (i = 0; i < N ; ++i)
{
free (tab[i]);
}
free (tab);
return;
}
short int canIGoDirection (short int valueOfNCase,
short int valueOfNPlusOneCase)
{
if (valueOfNCase != WALL)
{
if ((valueOfNCase == BOX && valueOfNPlusOneCase == EMPTY)
|| (valueOfNCase == BOX && valueOfNPlusOneCase == TARGET))
{
if (valueOfNPlusOneCase == TARGET)
{
// Box on target
return 3;
}
// move the box
return 2;
}
if ((valueOfNCase == BOX_ON_TARGET && valueOfNPlusOneCase == EMPTY)
|| (valueOfNCase == BOX_ON_TARGET
&& valueOfNPlusOneCase == TARGET))
{
if (valueOfNPlusOneCase == TARGET)
{
// Box on target and player on target too
return 6;
}
// move the box but player on a target
return 5;
}
if (valueOfNCase == TARGET)
{
// move player on target
return 4;
}
if (valueOfNCase == EMPTY)
{
// move player
return 1;
}
}
return 0;
}
void move (unsigned short int **tab, vect *playerPos, vect direction)
{
short int valueOfNCase
= tab[playerPos->x + direction.x][playerPos->y + direction.y];
vect add = plusVect (*playerPos, direction);
add = plusVect (add, direction);
short int valueOfNPlusOneCase;
if (add.x < 0 || add.y < 0)
{
valueOfNPlusOneCase = WALL;
}
else
{
valueOfNPlusOneCase = tab[add.x][add.y];
}
short int returnValue
= canIGoDirection (valueOfNCase, valueOfNPlusOneCase);
short int playerState = tab[playerPos->x][playerPos->y];
switch (returnValue)
{
case 0:
break;
case 1:
// move player
tab[playerPos->x + direction.x][playerPos->y + direction.y] = PLAYER;
break;
case 2:
// move player and the box
tab[playerPos->x + direction.x][playerPos->y + direction.y] = PLAYER;
tab[playerPos->x + direction.x * 2][playerPos->y + direction.y * 2]
= BOX;
break;
case 3:
// move player and the box is well-placed
tab[playerPos->x + direction.x][playerPos->y + direction.y] = PLAYER;
tab[playerPos->x + direction.x * 2][playerPos->y + direction.y * 2]
= BOX_ON_TARGET;
break;
case 4:
// move player on a target
tab[playerPos->x + direction.x][playerPos->y + direction.y]
= PLAYER_ON_TARGET;
break;
case 5:
// move box and player on a target
tab[playerPos->x + direction.x * 2][playerPos->y + direction.y * 2]
= BOX;
tab[playerPos->x + direction.x][playerPos->y + direction.y]
= PLAYER_ON_TARGET;
break;
case 6:
// move player on a target and box on a target
tab[playerPos->x + direction.x * 2][playerPos->y + direction.y * 2]
= BOX_ON_TARGET;
tab[playerPos->x + direction.x][playerPos->y + direction.y]
= PLAYER_ON_TARGET;
break;
default:
printf ("Commande inconnue !\n");
}
if (returnValue != 0)
{
if (playerState == PLAYER_ON_TARGET)
{
tab[playerPos->x][playerPos->y] = TARGET;
}
else
{
tab[playerPos->x][playerPos->y] = EMPTY;
}
playerPos->x = playerPos->x + direction.x;
playerPos->y = playerPos->y + direction.y;
}
}
void inGameLoop (unsigned short int **tab2d, int N, vect *playerPos,
vect *targets, int nbr_targets, dis *display_user)
{
char last = SDL_SCANCODE_1;
vect direction = { 0, 0 };
char input;
bool finish = false;
SDL_Event event;
while (!finish)
{
// printf ("Utilisez Z/Q/S/D pour bouger, X pour quitter : ");
SDL_PollEvent (&event);
if (event.type == SDL_KEYDOWN)
{
input = event.key.keysym.scancode;
switch (input)
{
case SDL_SCANCODE_ESCAPE:
finish = true;
break;
case SDL_SCANCODE_D:
direction.y = 1;
direction.x = 0;
break;
case SDL_SCANCODE_A:
direction.y = -1;
direction.x = 0;
break;
case SDL_SCANCODE_W:
direction.x = -1;
direction.y = 0;
break;
case SDL_SCANCODE_S:
direction.x = 1;
direction.y = 0;
break;
default:
direction.x = 0;
direction.y = 0;
last = SDL_SCANCODE_F21;
}
if (last != input)
move (tab2d, playerPos, direction);
last = input;
if (isWin (tab2d, targets, nbr_targets))
{
puts ("Win!");
finish = true;
}
if (islose (tab2d, N))
{
puts ("lose!");
finish = true;
}
screenDisplayGameSDL (tab2d, display_user);
}
last = SDL_SCANCODE_F21;
}
}
bool isWin (unsigned short int **tab2d, vect *targets, int nbr_targets)
{
int i;
for (i = 0; i < nbr_targets; ++i)
{
int x = targets[i].x;
int y = targets[i].y;
if (tab2d[x][y] != BOX_ON_TARGET)
{
return false;
}
}
return true;
}
bool islose (unsigned short int **tab2d, const int N)
{
int x = 0, y = 0;
for (x = 0; x < N; ++x)
{
for (y = 0; y < N; ++y)
{
bool isblock = false;
if (tab2d[x][y] == BOX)
{
vect box;
box.x = x;
box.y = y;
isblock = blockBox (tab2d, box);
}
if (isblock)
{
return true;
}
}
}
return false;
}
vect plusVect (vect one, vect two)
{
vect result;
result.x = one.x + two.x;
result.y = one.y + two.y;
return result;
}
int lengthVect (vect vector) { return abs (vector.x) + abs (vector.y); }
bool blockBox (unsigned short int **tab2d, vect box_coor)
{
int nbr_touch = 0;
vect card[4] = { { 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 } };
int indice_card[4] = { 0 };
int id_box = -1;
int i;
for (i = 0; i < 4; ++i)
{
vect test = plusVect (box_coor, card[i]);
unsigned short int val = tab2d[test.x][test.y];
if (val == BOX || val == WALL || val == BOX_ON_TARGET)
{
nbr_touch++;
if (val == BOX || val == BOX_ON_TARGET)
{
id_box = nbr_touch - 1;
}
indice_card[nbr_touch - 1] = i;
}
}
if (nbr_touch == 0)
return false;
if (nbr_touch >= 3)
{
if (id_box != -1)
{
vect other1 = card[indice_card[(id_box + 1) % 3]];
vect other2 = card[indice_card[(id_box + 2) % 3]];
vect test_add_other = plusVect (other1, other2);
vect coor_box = plusVect (card[indice_card[id_box]], box_coor);
vect other_plus_box1 = plusVect (other1, coor_box);
vect other_plus_box2 = plusVect (other2, coor_box);
int val1 = tab2d[other_plus_box1.x][other_plus_box1.y];
int val2 = tab2d[other_plus_box2.x][other_plus_box2.y];
if (test_add_other.x == 0 && test_add_other.y == 0)
{
if ((val1 == WALL || val1 == BOX || val1 == BOX_ON_TARGET)
|| (val2 == WALL || val2 == BOX || val2 == BOX_ON_TARGET))
{
return true;
}
return false;
}
}
return true;
}
if (nbr_touch == 2)
{
int id1 = indice_card[0];
int id2 = indice_card[1];
vect add = plusVect (card[id1], card[id2]);
if (lengthVect (add) == 2)
{
if (id_box == -1)
return true;
vect test = plusVect (card[indice_card[id_box]],
card[indice_card[(id_box + 1) % 2]]);
test = plusVect (test, box_coor);
if (tab2d[test.x][test.y] == WALL)
return true;
}
return false;
}
vect normal = card[indice_card[0]];
vect director1 = card[(indice_card[0] + 1) % 4];
vect director2 = card[(indice_card[0] + 1) % 4];
vect dir_test[2] = { director1, director2 };
bool wall = false;
for (int i = 0; i < 2; ++i)
{
vect current = plusVect (box_coor, dir_test[i]);
while (!wall)
{
unsigned short int val = tab2d[current.x][current.y];
if (val == WALL)
wall = true;
if (val == TARGET)
return false;
vect cur_nor = plusVect (current, normal);
val = tab2d[cur_nor.x][cur_nor.y];
if (val != WALL || val != BOX || val != BOX_ON_TARGET)
{
return false;
}
}
}
return true;
perror ("error in blockBox");
exit (0);
}