42seoul:: So_long

jahlee·2023년 2월 4일
0

개인 공부

목록 보기
6/23
post-thumbnail

perror

오류 메세지를 출력해주는 함수로 전역 변수 errno의 값을 해석하여 이에 해당하는 시스템 오류 메세지를 표준 오류 출력 스트림 (stderr)에 출력한다. 또한 추가적으로 전달하고자 하는 사용자 정의 메세지를 str 인자에 담아 출력할 수 도 있다.

#include <stdio.h>
int main() 
{
	FILE* pFile;
	pFile = fopen("unexist.ent", "rb");
	if (pFile == NULL)
		perror("The following error occurred");
	else
		fclose(pFile);
	return 0;
}
/*-----------------------result------------------------*/
/*
	The following error occurred: No such file or directory
*/

strerror

#include <string.h>
    
char* strerror(int errnum);

오류 메세지 문자열을 가리키는 포인터를 얻어온다. errnum의 값을 통해 발생하였던 오류에 알맞은 오류 메세지를 가리키는 포인터를 리턴해준다.

parse한 map의 조건

  1. 벽으로 둘러싸여야 함
  2. 최소 출구(E) 1, 수집품(c)1, 시작지점(P) 1 // E,P는 무조건 한개
  3. 탈출가능해야함
  4. 직사각형
  5. 파싱하는 맵이 .ber파일인지도 확인하자!
  • floos fill 방식으로 구현(dfs)
    수집품을 찾으면서 탈출하는 최소 경로를 탐색해야한다.
  • 구현
    1. 파싱
    2. 파싱한 맵 검사
      1. 벽둘러 쌓였는지 확인
      2. 최소 출구(E) 1, 수집품(c)1, 시작지점(P) 1 // E,P는 무조건 한개. 각 위치 좌표 저장!
      3. 직사각형인지
      4. 수집품 다 모으고 탈출 가능한지
        수집품 방문체크하고(탈출지점은 벽과 동일하게 생각) 탈출지점 동서남북 방문했으면 탈출 가능

주요 구현 코드

read 함수로 정해준 버퍼사이즈 만큼 읽어주고 만약 파일을 다읽지 않았다면 계속 이어붙여서 하나의 char *로 만들어 준다. 이후 split을 사용하여 char **로 연결시켜주면 된다. 주의할점은 기존에 구현했던 split함수가 개행을 기준으로 나누어주는 함수 이기때문에 파싱하는 맵이 중간에 개행으로 나누어진 잘못된 맵이어도 이어서 정상출력을 해주게된다. 이점을 따로 잡아주면 된다.

void	parse_map(t_game *game)
{
	int		read_byte;
	char	*res;
	char	*tmp;

	res = NULL;
	while (1)
	{
		tmp = (char *)malloc(sizeof(char) * (BUFFER_SIZE + 1));
		read_byte = read(game->fd, tmp, BUFFER_SIZE);
		if (read_byte == -1)
		{
			free(tmp);
			err_free(&game, &res, "Read Error");
		}
		tmp[read_byte] = '\0';
		res = ft_strjoin_free(&res, &tmp);
		if (read_byte < BUFFER_SIZE)
			break ;
	}
	if (check_double_nl(res))
		err_free(&game, &res, "Invalid Map : nl");
	game->map = ft_split(res, '\n');
	check_map(res, game);
}

파싱한 맵이 유효한 맵인지 검사를 해준다.

void	check_map(char *res, t_game *game)
{
	if (res[0] == '\0')
		err_free(&game, &res, "Invalid Map : Empty Map");
	if (res)
		free(res);
	if (!game->map)
		err_free(&game, NULL, "Invalid Map : ft_split Error");
	is_mapsquare(game); // 직사각형인지
	is_wallaround(game); // 벽으로 둘러쌓였는지
	check_cep(game, 0, 0); // 인자들 조건에 따라 확인
	can_escape(game); // 탈출 가능한지
}

맵을 dfs를 통해 벽이나 출구가 아닐경우 방문체크를 해주며 vis배열을 채워준다.
이후 vis배열과 map를 비교해 준다. 예시로 수집품이 있는 자리인데 방문 체크가 안된다면 수집품을 다 먹지 못하는 것이고, 출구인데 동서남북 방향으로 방문한적이 없다면 탈출할 수 없는 잘못된 맵이라는 점을 알 수 있다.

static int	ewsn(int idx)
{
	if (idx % 4 == 3)
		return (0);
	return (idx % 4 - 1);
}

static void	check_vis(t_game *game)
{
	int	x;
	int	y;
	int	idx;

	x = -1;
	while (++x < game->map_height)
	{
		y = -1;
		while (++y < game->map_width)
		{
			if (game->map[x][y] == 'C' && game->vis[x][y] != '1')
				err_free(&game, NULL, "Invalid Map : Can't Collect");
			else if (game->map[x][y] == 'E')
			{
				idx = -1;
				while (++idx < 4)
				{
					if (game->vis[x + ewsn(idx)][y + ewsn(idx + 1)] == '1')
						break ;
				}
				if (idx == 4)
					err_free(&game, NULL, "Invalid Map : Can't Escape");
			}
		}
	}
}

주어진 mlx init 함수를 사용하여 초기화를 시켜주고
mlx_new_window 함수를 사용하여 윈도우 창을 띄워준다.
이후 xpm파일로 변환한 png를 mlx_xpm_file_to_image 함수를 사용하여 포인터로 이미지를 등록해준다.
해당 이미지를 맵구조에 맞게 윈도우 창에 띄워주면 되는데 이때는 mlx_put_image_to_window함수를 사용해주면 된다.
mlx_hook 함수는 매개변수로 주어진 키를 눌렀을때 매개변수로 들어가는 함수를 실행시켜주는데 방향키 지정과 종료조건등의 설정을 해줄 수 있다.
모든 설정을 다하였다면 mlx_loop함수를 사용하여 게임을 실행 시키면 된다.

void	map_to_window(t_img *img, t_game *game)
{
	img->mlx = mlx_init();
	img->win = mlx_new_window(img->mlx, game->map_width * 32, \
	game->map_height * 32, "mlx_test");
	img->p = mlx_xpm_file_to_image(img->mlx, "./textures/player.xpm", \
	&img->img_w, &img->img_h);
	mlx_hook(img->win, 17, 0, normal_exit, game);
	mlx_hook(img->win, X_EVENT_KEY_PRESS, 0, &key_press, game);
	mlx_loop(img->mlx);
}

!!!!주의할 점들!!!

창에 띄워지는 좌표와 맵의 좌표가 상하반전인 경우여서 조건들을 부여할 떄 조심해야한다.

0개의 댓글