프로젝트/42Seoul
42서울 본과정 - So_Long (구현)
soohykim
2025. 4. 11. 10:30
728x90
반응형
📘 구현
📌 순서
- (1) Window 생성
- (2) map 생성
- window에 이미지 넣기
- map.ber 파일 읽기
- map 정보에 맞게 이미지 변환
- img 크기만큼 window 조정
- (3) 키 입력 이벤트
- 키 입력 받고 출력
- 키 입력 이벤트 받고 동작
- 키 이동 횟수 출력
- map 규칙 적용
- 종료 버튼 누르면 신호 수신 후 동작
- (4) map 규칙 체크 (에러 처리)
- 지도 직사각형 아닌 경우
- 지도 벽에 둘러싸여 있지 않은 경우
- 지도 출구/시작지점/수집품 없는 경우
- 지도 지정되지 않은 문자가 포함된 경우
- 비어있는 맵이 포함된 경우
- 출구 뒤에 아이템이 있는 경우
- map.ber 파일 확장자 틀린 경우
- (5) map 경로 확인
📌 컴파일 주의
- iMac : cc -lmlx -framework OpenGL -framework AppKit *.c
- MacBook (M2) : cc -L./mlx -lmlx -framework OpenGL -framework AppKit *.c
📌 사전 준비
- subject의 minilibx_opengl.tgz 폴더 다운로드
- 과제 폴더에 mlx 이름으로 변경하여 추가
- Makefile 수정
%.o:%.c so_long.h
cc -Wall -Wextra -Werror -c $< -o $@
$(NAME) : $(OBJS)
make -C ./mlx/
cc -o so_long $(OBJS) -L./mlx -lmlx -framework OpenGL -framework AppKit
📌 1. Window 생성
사용 함수
- void* mlx_init(void)
- SW와 OS의 디스플레이 연결
- void* mlx_new_window (mlx 포인터, x 사이즈, y 사이즈, window title명)
- 디스플레이에 새로운 window 띄우는 함수
- int mlx_loop(void* mlx 포인터)
- 새로운 window에서 키보드와 마우스 입력 대기
#include "./mlx/mlx.h"
int main(void)
{
void *mlx;
void *win;
mlx = mlx_init();
win = mlx_new_window(mlx, 500, 500, "so_long");
mlx_loop(mlx);
}
📌 2. map 생성
(1) window에 이미지 넣기
사용 함수
- void* mlx_xpm_file_to_image(mlx 포인터, 파일이름, 너비, 높이)
- 이미지 가져와서 메모리에 올리고, 메모리 주소 반환
- int mlx_put_image_to_window(mlx 포인터, window 포인터, image 포인터, x좌표, y좌표)
- 이미지 포인터 받아서, 윈도우 안에 좌표를 지정하여 이미지 띄움
-png이미지 파일 변환시 64픽셀로 고정함
- 이미지 포인터 받아서, 윈도우 안에 좌표를 지정하여 이미지 띄움
#include "so_long.h"
void init(t_param *p)
{
p->play_x = 3;
p->play_y = 4;
p->mlx = mlx_init();
p->win = mlx_new_window(p->mlx, 500, 500, "so_long");
p->img_coin = mlx_xpm_file_to_image(p->mlx, "./imgs/coin.xpm", &p->img_x, &p->img_y);
p->img_exit = mlx_xpm_file_to_image(p->mlx, "./imgs/exit.xpm", &p->img_x, &p->img_y);
p->img_map = mlx_xpm_file_to_image(p->mlx, "./imgs/map.xpm", &p->img_x, &p->img_y);
p->img_mario = mlx_xpm_file_to_image(p->mlx, "./imgs/mario.xpm", &p->img_x, &p->img_y);
p->img_wall = mlx_xpm_file_to_image(p->mlx, "./imgs/wall.xpm", &p->img_x, &p->img_y);
}
int main(void)
{
t_param p;
init(&p);
mlx_put_image_to_window(p.mlx, p.win, p.img_wall, 0, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_map, 64, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_coin, 128, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_exit, 0, 128);
mlx_put_image_to_window(p.mlx, p.win, p.img_mario, 0, 64);
mlx_loop(p.mlx);
}
(2) map.ber 파일 읽기
- map_read
- open 함수 이용하여 파일 열기
- gnl 이용하여 1줄씩 line에 저장
- 해당 부분에서 line의 수를 return 하려고 했으나 main에서 받으면 계속 segmentation fault 오류 떠서 실패
- map_strjoin
- new를 malloc해서 연결리스트에 저장
int map_strjoin(t_param *p, char *str)
{
t_list *cur;
t_list *new;
cur = p->map;
new = malloc(sizeof(t_list));
if (!new) {
map_free(p);
return (1);
}
new->data = ft_strdup(str);
new->next = NULL;
if (cur == NULL)
p->map = new;
else {
while (cur->next != NULL)
cur = cur->next;
cur->next = new;
}
return (0);
}
int map_read(t_param *p, char *file)
{
int fd;
char *line;
fd = open(file, O_RDONLY);
if (fd <= 0)
return (1);
line = get_next_line(fd);
ft_printf("line = %s\n", line);
while (line != NULL)
{
if (map_strjoin(p, line))
return (1);
free(line);
line = get_next_line(fd);
ft_printf("line = %s\n", line);
}
free(line);
return (0);
}
void map_free(t_param *p)
{
t_list *cur;
t_list *new;
cur = p->map;
while (cur)
{
new = cur->next;
free (cur->data);
free (cur);
cur = new;
}
free (cur);
}
int main(void)
{
t_param p;
init(&p);
map_read(&p, "map/map.ber");
mlx_put_image_to_window(p.mlx, p.win, p.img_wall, 0, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_map, 64, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_coin, 128, 0);
mlx_put_image_to_window(p.mlx, p.win, p.img_exit, 0, 128);
mlx_put_image_to_window(p.mlx, p.win, p.img_mario, 0, 64);
mlx_loop(p.mlx);
}
(3) map 정보에 맞게 이미지 변환
void map_set(t_param *p)
{
t_list *cur;
char *line;
int x;
int y;
x = 0;
y = 0;
cur = p->map;
init_player(p);
mlx_clear_window(p->mlx, p->win);
while (cur)
{
line = cur->data;
while (*line)
{
map_imgs(p, *line, x, y);
line++;
x += 64;
}
x = 0;
y += 64;
cur = cur->next;
}
}
void map_imgs(t_param *p, char c, int x, int y)
{
mlx_put_image_to_window(p->mlx, p->win, p->img_map, x, y);
if (c == '1')
mlx_put_image_to_window(p->mlx, p->win, p->img_wall, x, y);
else if (c == '0')
mlx_put_image_to_window(p->mlx, p->win, p->img_map, x, y);
else if (c == 'C')
mlx_put_image_to_window(p->mlx, p->win, p->img_coin, x, y);
else if (c == 'E')
mlx_put_image_to_window(p->mlx, p->win, p->img_exit, x, y);
else if (c == 'P')
mlx_put_image_to_window(p->mlx, p->win, p->img_mario, x, y);
}
void init_player(t_param *p)
{
t_list *cur;
char *line;
cur = p->map;
while (cur)
{
line = cur->data;
while (*line)
{
if (*line++ == 'P')
return ;
p->play_x++;
}
p->play_x = 0;
p->play_y++;
cur = cur->next;
}
}
(수정) 뒤에 map 배경 모두 설정한 후 이미지 출력
(4) img 크기만큼 window 조정
int main(void)
{
t_param p;
init(&p);
if (map_read(&p, "map/map.ber"))
{
ft_printf("Error : Map Read Fail");
map_free(&p);
exit(0);
return (0);
}
map_check(&p);
p.win = mlx_new_window(p.mlx, p.win_x, p.win_y, "so_long");
map_set(&p);
mlx_key_hook(p.win, &key_press, &p);
mlx_hook(p.win, RED, 0, &map_red, &p);
mlx_loop(p.mlx);
return (0);
}
📌 3. 키 입력 이벤트
사용 함수
- int mlx_hook(win 포인터, event, mask, 호출할 함수 포인터, 함수에 전달할 파라미터)
- event : X11 event
- mask : mac에서는 미사용으로 0 입력
(1) 키 입력 받고 출력
- WASD : 상하좌우
- ESC 및 좌측 빨간 버튼 : 종료
#include "./mlx/mlx.h"
#include "./ft_printf/ft_printf.h"
# define KEY_PRESS 2
# define KEY_RELEASE 3
# define KEY_W 13
# define KEY_A 0
# define KEY_S 1
# define KEY_D 2
# define KEY_ESC 53
# define KEY_RED 17
typedef struct s_param
{
int x;
int y;
} t_param;
void init(t_param *p)
{
p->x = 3;
p->y = 4;
}
int key_press(int key, t_param *p)
{
if (key == KEY_W)
key_W(p);
else if (key == KEY_A)
key_A(p);
else if (key == KEY_S)
key_S(p);
else if (key == KEY_D)
key_D(p);
else if (key == KEY_ESC)
exit(0);
else if (key == KEY_RED)
exit(0);
ft_printf("x : %d, y : %d\n", p->x, p->y);
return (0);
}
void key_W(t_param *p)
{
p->y++;
}
void key_A(t_param *p)
{
p->x--;
}
void key_S(t_param *p)
{
p->y--;
}
void key_D(t_param *p)
{
p->x++;
}
int main(void)
{
void *mlx;
void *win;
t_param p;
init(&p);
mlx = mlx_init();
win = mlx_new_window(mlx, 500, 500, "so_long");
mlx_hook(win, KEY_RELEASE, 0, &key_press, &p);
mlx_loop(mlx);
if (win == 0)
return(0);
}
(2) 키 입력 이벤트 받고 동작 / 키 이동 횟수 출력
- 현재 : line의 'P' ➡ '0'으로 변경
- 이동 : line의 그 외 ➡ 'P'으로 변경
- 이동 횟수 : move 저장
// 마리오(P)가 지나가면 벽(1), 코인(C), 출구(E) 모두 맵(0)으로 바꿈
#include "so_long.h"
int key_press(int key, t_param *p)
{
if (key == W)
key_W(p);
else if (key == A)
key_A(p);
else if (key == S)
key_S(p);
else if (key == D)
key_D(p);
else if (key == ESC)
map_esc(p);
ft_printf("move = %d\n", p->move);
map_set(p);
return (0);
}
void key_A(t_param *p)
{
int y;
char *line;
t_list *cur;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
line[p->play_x - 1] = 'P';
line[p->play_x] = '0';
init_player(p);
p->move++;
}
(3) map 규칙 적용
- wall일 경우 : 이동 금지
- exit일 경우 : coin = 0이면 종료
- coin일 경우 : coin-- 후 이동
int key_press(int key, t_param *p)
{
if (key == W)
key_up(p);
else if (key == A)
key_left(p);
else if (key == S)
key_down(p);
else if (key == D)
key_right(p);
else if (key == ESC)
map_esc(p);
else
return (1);
ft_printf("move = %d\n", p->move);
map_set(p);
return (0);
}
void key_up(t_param *p)
{
int y;
char *line;
t_list *cur;
char *cur_prev;
cur = p->map;
y = p->play_y;
while (y-- != 1)
cur = cur->next;
cur_prev = cur->data;
cur = cur->next;
line = cur->data;
if (cur_prev[p->play_x] == '1')
return ;
else if (cur_prev[p->play_x] == 'E')
if (map_exit(p))
return ;
if (cur_prev[p->play_x] == 'C')
p->coin--;
cur_prev[p->play_x] = 'P';
line[p->play_x] = '0';
init_player(p);
p->move++;
}
void key_left(t_param *p)
{
int y;
char *line;
t_list *cur;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
if (line[p->play_x - 1] == '1')
return ;
else if (line[p->play_x - 1] == 'E')
if (map_exit(p))
return ;
if (line[p->play_x - 1] == 'C')
p->coin--;
line[p->play_x - 1] = 'P';
line[p->play_x] = '0';
init_player(p);
p->move++;
}
void key_down(t_param *p)
{
int y;
char *line;
t_list *cur;
char *cur_next;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
cur = cur->next;
cur_next = cur->data;
if (cur_next[p->play_x] == '1')
return ;
else if (cur_next[p->play_x] == 'E')
if (map_exit(p))
return ;
if (cur_next[p->play_x] == 'C')
p->coin--;
line[p->play_x] = '0';
cur_next[p->play_x] = 'P';
init_player(p);
p->move++;
}
void key_right(t_param *p)
{
int y;
char *line;
t_list *cur;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
if (line[p->play_x + 1] == '1')
return ;
else if (line[p->play_x + 1] == 'E')
if (map_exit(p))
return ;
if (line[p->play_x + 1] == 'C')
p->coin--;
line[p->play_x + 1] = 'P';
line[p->play_x] = '0';
init_player(p);
p->move++;
}
(4) 종료 버튼 누르면 신호 수신 후 동작
// RED 버튼 누르면 segmentation fault 오류 발생
mlx_hook(p.win, RED, 0, &map_exit, &p);
int map_exit(int key, t_param *p)
{
if (key == ESC)
ft_printf("Exit : ESC");
else
ft_printf("Exit : Red Button");
map_free(p);
exit(0);
return (0);
}
// 수정 완료
mlx_hook(p.win, RED, 0, &map_red, &p);
int map_esc(t_param *p)
{
ft_printf("Exit : ESC");
map_free(p);
exit(0);
return (0);
}
int map_red(t_param *p)
{
ft_printf("Exit : Red Button");
map_free(p);
exit(0);
return (0);
}
📌 4. map 규칙 체크
(1) 지도 직사각형 아닌 경우
- map.ber 파일에 비어있는 줄이 포함된 경우
int map_check(t_param *p)
{
t_list *cur;
int count;
cur = p->map;
count = map_check_line(p);
if (count == -1)
return (1);
p->win_x = p->img_x * (ft_strlen(cur->data) - 1);
p->win_y = p->img_y * count;
if (map_check_wall(p, count) == -1)
{
ft_printf("Error : (No Wall Enclosed) ");
return (1);
}
if (map_check_count(p) == -1)
{
ft_printf("Error : (Invalid Start / Exit / Collect) ");
return (1);
}
return (0);
}
int map_check_line(t_param *p)
{
t_list *cur;
int len;
int len_next;
int count;
cur = p->map;
len = ft_strlen(cur->data);
count = 0;
while (cur)
{
len_next = ft_strlen(cur->data);
if (len != len_next)
{
ft_printf("Error : (Different Line Lengths) ");
return (-1);
}
cur = cur->next;
count++;
}
if (count < 3)
{
ft_printf("Error : (Less than 3 Lines) ");
return (-1);
}
return (count);
}
(2) 지도 벽에 둘러싸여있지 않은 경우
int map_check_wall(t_param *p, int end)
{
int count;
t_list *cur;
count = 0;
cur = p->map;
while (cur)
{
if (check_wall(cur->data, count, end) == -1)
return (-1);
cur = cur->next;
count++;
}
return (0);
}
int check_wall(char *str, int count, int end)
{
char c;
if (count == 0 || count == end - 1)
{
while (*str != '\n')
{
if (*str != '1')
return (-1);
str++;
}
}
else
{
if (*str != '1')
return (-1);
while (*str != '\n')
{
c = *str;
str++;
}
if (c != '1')
return (-1);
}
return (0);
}
(3) 지도 출구/시작지점/수집품 없는 경우
- 맞을 경우 : 플레이어 시작 위치 저장
- 틀릴 경우 : 에러 메시지 표출 + 할당된 메모리 free + 종료
int map_check_count(t_param *p)
{
t_list *cur;
char *line;
cur = p->map;
while (cur)
{
line = cur->data;
while (*line)
{
if (*line == 'C')
p->coin++;
else if (*line == 'E')
p->exit++;
else if (*line == 'P')
p->player++;
line++;
}
cur = cur->next;
}
if (p->player != 1 || p->exit != 1 || p->coin < 1)
return (-1);
return (0);
}
(4) 지도 지정되지 않은 문자가 포함된 경우
- 특수문자 및 map 규칙 이외의 문자 입력할 경우
// map_imgs 에서 확인 후 에러 처리
void map_imgs(t_param *p, char c)
{
if (c != '1' && c != '0' && c != 'C' && c != 'E' && c != 'P' && c != '\n' && c != '\0')
{
printf("%c\n", c);
ft_printf("Error : Invalid Char\n");
map_free(p);
exit(0);
}
...
}
(5) 비어있는 맵이 포함된 경우
- 에러 처리 안할경우 Segmentation Fault 오류 발생
int map_read(t_param *p, char *file)
{
...
fd = open(file, O_RDONLY);
if (fd < 0)
return (1);
line = get_next_line(fd);
if (line == NULL) // 비어있어서 line = 0일 경우 에러 발생
{
free(line);
ft_printf("Empty Map\n");
return (1);
}
}
(6) 출구 뒤에 아이템이 있는 경우
int map_dfs(t_param *p, int x, int y, char visit[2000][2000])
{
...
else if (visit[y][x] == 'E') // 출구 만나면 재귀 빠져나오게 변경
{
p->exit++;
return (0);
}
...
}
(7) map.ber 파일 확장자 틀린 경우
- 프로그램 인자로 .ber 만을 받아야 함
int file_name(char *file)
{
int i;
char *ext;
i = 0;
ext = ".ber";
while (*(file + i) != '.')
i++;
while (*(file + i) || *ext)
{
if (*(file + i) != *ext)
{
ft_printf_s("Error : Invalid File name or File Extenstion\n");
return (1);
}
ext++;
i++;
}
return (0);
}
📌 5. map 경로 확인
- dfs(깊이우선탐색) : dfs란 전체 탐색을 하는 알고리즘 중 하나로 하나의 경로로 쭉 파고들어 끝까지 확인한 후 다시 이전으로 돌아와서 다음경로를 끝까지 파고든다 이러한 동작은 모든 장소를 확인할때 까지 반복해서 동작한다
int dfs(t_param *p, int coin, int exit)
{
t_list *cur;
char *line;
char d[2000][2000];
int x;
int y;
cur = p->map;
y = 0;
while (cur)
{
x = 0;
line = cur->data;
while (*line)
d[y][x++] = *line++;
y++;
cur = cur->next;
}
map_dfs(p, p->play_x, p->play_y, d);
if (p->exit != 2 * exit || p->coin != 2 * coin)
{
ft_printf("Error : (No Map Route) ");
return (1);
}
p->coin = coin;
return (0);
}
int map_dfs(t_param *p, int x, int y, char visit[2000][2000])
{
if (x < 0 || x >= p->win_x || y < 0 || y >= p->win_y)
return (0);
if (visit[y][x] == '1')
return (0);
if (visit[y][x] != 'V')
{
if (visit[y][x] == 'E')
p->exit++;
if (visit[y][x] == 'C')
p->coin++;
visit[y][x] = 'V';
map_dfs(p, x - 1, y, visit);
map_dfs(p, x + 1, y, visit);
map_dfs(p, x, y - 1, visit);
map_dfs(p, x, y + 1, visit);
return (1);
}
return (0);
}
📘 코드
📌 so_long.c
- init : 구조체 변수 초기화
- init_player : 플레이어 위치를 play_x, play_y에 저장
- map_exit : coin 개수가 0일 경우에 종료
- map_red_exit : 좌측 상단 빨간 버튼 눌렀을 경우 종료
- main : map_read ➡ map_check ➡ map_set ➡ dfs
#include "so_long.h"
int init(t_param *p)
{
p->mlx = mlx_init();
p->img_coin = mlx_xpm_file_to_image(\
p->mlx, "imgs/coin.xpm", &p->x, &p->y);
p->img_exit = mlx_xpm_file_to_image(\
p->mlx, "imgs/exit.xpm", &p->x, &p->y);
p->img_map = mlx_xpm_file_to_image(\
p->mlx, "imgs/map.xpm", &p->x, &p->y);
p->img_mario = mlx_xpm_file_to_image(\
p->mlx, "imgs/mario.xpm", &p->x, &p->y);
p->img_wall = mlx_xpm_file_to_image(\
p->mlx, "imgs/wall.xpm", &p->x, &p->y);
p->map = NULL;
p->move = 1;
p->count = 0;
p->coin = 0;
p->coin_check = 0;
p->exit = 0;
p->player = 0;
return (1);
}
void init_player(t_param *p)
{
t_list *cur;
char *line;
cur = p->map;
p->play_x = 0;
p->play_y = 0;
while (cur)
{
line = cur->data;
while (*line)
{
if (*line++ == 'P')
return ;
p->play_x++;
}
p->play_x = 0;
p->play_y++;
cur = cur->next;
}
}
int map_exit(t_param *p)
{
if (p->coin == 0)
{
p->move++;
ft_printf_d(p->move);
ft_printf_s("Exit : GAME SUCCESS\n");
map_free(p);
exit(0);
return (0);
}
return (1);
}
int map_red_exit(t_param *p)
{
ft_printf_s("Exit : Red Button");
map_free(p);
exit(0);
return (1);
}
int main(int argc, char **argv)
{
t_param p;
if (argc != 2 || file_name(argv[1]))
{
ft_printf_s("Error : Few argument or Invalid File Extenstion\n");
return (-1);
}
if (init(&p) && map_read(&p, argv[1]))
return (-1);
if (map_check(&p))
{
map_free(&p);
return (-1);
}
p.win = mlx_new_window(p.mlx, p.win_x, p.win_y, "so_long");
map_set(&p);
if (dfs(&p))
{
map_free(&p);
return (-1);
}
mlx_key_hook(p.win, &move, &p);
mlx_hook(p.win, RED, 0, &map_red_exit, &p);
mlx_loop(p.mlx);
return (0);
}
📌 so_long_map.c
- map_read : 파일 한 줄씩 읽기
- map_strjoin : 읽은 line을 연결리스트 마지막에 연결
- map_set : line을 한개씩 map_imgs 인자로 넘긴 후 64픽셀씩 x, y에 더하기
- map_imgs : 인자에 맞는 이미지를 x, y의 위치에 표출 (정해지지 않은 인자 에러처리)
- map_free : 동적할당된 연결리스트 안의 내용 해제
#include "so_long.h"
int map_read(t_param *p, char *file)
{
int fd;
char *line;
fd = open(file, O_RDONLY);
if (fd < 0)
{
ft_printf_s("Error : Read Fail or Invalid File name\n");
return (1);
}
line = get_next_line(fd);
if (line == NULL)
{
free(line);
ft_printf_s("Error : Invalid Map (Empty Map)\n");
return (1);
}
while (line != NULL)
{
map_strjoin(p, line);
free(line);
line = get_next_line(fd);
}
free(line);
return (0);
}
int map_strjoin(t_param *p, char *line)
{
t_list *cur;
t_list *new;
cur = p->map;
new = malloc(sizeof(t_list));
if (!new)
{
map_free(p);
return (0);
}
new->data = ft_strdup(line);
new->next = NULL;
if (cur == NULL)
p->map = new;
else
{
while (cur->next != NULL)
cur = cur->next;
cur->next = new;
}
return (1);
}
void map_set(t_param *p)
{
t_list *cur;
char *line;
p->x = 0;
p->y = 0;
cur = p->map;
init_player(p);
mlx_clear_window(p->mlx, p->win);
while (cur)
{
line = cur->data;
while (*line)
{
map_imgs(p, *line);
line++;
p->x += 64;
}
p->x = 0;
p->y += 64;
cur = cur->next;
}
}
void map_imgs(t_param *p, char c)
{
if (c != '1' && c != '0' && c != 'C' && c != 'E' && c != 'P' \
&& c != '\n' && c != '\0')
{
ft_printf_s("Error : Invalid Char\n");
map_free(p);
exit(0);
}
mlx_put_image_to_window(\
p->mlx, p->win, p->img_map, p->x, p->y);
if (c == '1')
mlx_put_image_to_window(\
p->mlx, p->win, p->img_wall, p->x, p->y);
else if (c == '0')
mlx_put_image_to_window(\
p->mlx, p->win, p->img_map, p->x, p->y);
else if (c == 'C')
mlx_put_image_to_window(\
p->mlx, p->win, p->img_coin, p->x, p->y);
else if (c == 'E')
mlx_put_image_to_window(\
p->mlx, p->win, p->img_exit, p->x, p->y);
else if (c == 'P')
mlx_put_image_to_window(\
p->mlx, p->win, p->img_mario, p->x, p->y);
}
void map_free(t_param *p)
{
t_list *cur;
t_list *new;
cur = p->map;
while (cur)
{
new = cur->next;
free (cur->data);
free (cur);
cur = new;
}
free (cur);
exit(0);
}
📌 so_long_move.c
- move : 키가 눌렸을 때 알맞게 분기 후 map 다시 그리기
- move_up
- move_left
- move_down
- move_right
#include "so_long.h"
int move(int key, t_param *p)
{
if (key == W)
move_up(p);
else if (key == A)
move_left(p);
else if (key == S)
move_down(p);
else if (key == D)
move_right(p);
else if (key == ESC)
{
ft_printf_s("Exit : ESC");
map_free(p);
exit(0);
}
else
return (1);
map_set(p);
return (0);
}
void move_up(t_param *p)
{
int y;
char *line;
t_list *cur;
char *cur_prev;
cur = p->map;
y = p->play_y;
while (y-- != 1)
cur = cur->next;
cur_prev = cur->data;
cur = cur->next;
line = cur->data;
if (cur_prev[p->play_x] == '1')
return ;
else if (cur_prev[p->play_x] == 'E')
if (map_exit(p))
return ;
if (cur_prev[p->play_x] == 'C')
p->coin--;
cur_prev[p->play_x] = 'P';
line[p->play_x] = '0';
init_player(p);
ft_printf_d(p->move);
p->move++;
}
void move_left(t_param *p)
{
int y;
char *line;
t_list *cur;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
if (line[p->play_x - 1] == '1')
return ;
else if (line[p->play_x - 1] == 'E')
if (map_exit(p))
return ;
if (line[p->play_x - 1] == 'C')
p->coin--;
line[p->play_x - 1] = 'P';
line[p->play_x] = '0';
init_player(p);
ft_printf_d(p->move);
p->move++;
}
void move_down(t_param *p)
{
int y;
char *line;
t_list *cur;
char *cur_next;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
cur = cur->next;
cur_next = cur->data;
if (cur_next[p->play_x] == '1')
return ;
else if (cur_next[p->play_x] == 'E')
if (map_exit(p))
return ;
if (cur_next[p->play_x] == 'C')
p->coin--;
line[p->play_x] = '0';
cur_next[p->play_x] = 'P';
init_player(p);
ft_printf_d(p->move);
p->move++;
}
void move_right(t_param *p)
{
int y;
char *line;
t_list *cur;
cur = p->map;
y = p->play_y;
while (y--)
cur = cur->next;
line = cur->data;
if (line[p->play_x + 1] == '1')
return ;
else if (line[p->play_x + 1] == 'E')
if (map_exit(p))
return ;
if (line[p->play_x + 1] == 'C')
p->coin--;
line[p->play_x + 1] = 'P';
line[p->play_x] = '0';
init_player(p);
ft_printf_d(p->move);
p->move++;
}
📌 so_long_check.c
- map_check : check_line ➡ check_wall ➡ check_count
- map_check_line : 이전 및 현재 line의 길이 체크
- map_check_wall : 벽에 둘러싸여 있는지 체크
- map_check_count : P(플레이어) & E(탈출구)가 1개인지 + C(수집품)이 1개 이상인지 체크
#include "so_long.h"
int map_check(t_param *p)
{
t_list *cur;
cur = p->map;
if (map_check_line(p) == -1)
return (1);
p->win_x = p->x * (ft_strlen(cur->data) - 1);
p->win_y = p->y * p->count;
if (map_check_wall(p, p->count) == -1)
{
ft_printf_s("Error : Invalid Map (No Wall Enclosed) \n");
return (1);
}
if (map_check_count(p) == -1)
{
ft_printf_s("Error : Invalid Map (Invalid Start / Exit / Collect) \n");
return (1);
}
return (0);
}
int map_check_line(t_param *p)
{
t_list *cur;
int len;
int len_next;
cur = p->map;
len = ft_strlen(cur->data);
while (cur)
{
len_next = ft_strlen(cur->data);
if (len != len_next)
{
ft_printf_s("Error : Invalid Map (Different Line Lengths) \n");
return (-1);
}
cur = cur->next;
p->count++;
}
if (p->count < 3)
{
ft_printf_s("Error : Invalid Map (Less than 3 Lines) \n");
return (-1);
}
return (1);
}
int map_check_wall(t_param *p, int end)
{
int count;
t_list *cur;
count = 0;
cur = p->map;
while (cur)
{
if (check_wall(cur->data, count, end) == -1)
return (-1);
cur = cur->next;
count++;
}
return (0);
}
int check_wall(char *str, int count, int end)
{
char c;
if (count == 0 || count == end - 1)
{
while (*str != '\n')
if (*str++ != '1')
return (-1);
}
else
{
if (*str != '1')
return (-1);
while (*str != '\n')
c = *str++;
if (c != '1')
return (-1);
}
return (0);
}
int map_check_count(t_param *p)
{
t_list *cur;
char *line;
cur = p->map;
while (cur)
{
line = cur->data;
while (*line)
{
if (*line == 'C')
p->coin++;
else if (*line == 'E')
p->exit++;
else if (*line == 'P')
p->player++;
line++;
}
cur = cur->next;
}
if (p->player != 1 || p->exit != 1 || p->coin < 1)
return (-1);
return (0);
}
📌 so_long_dfs.c
- dfs : visit 2차원 배열 할당받아 map 복사 후 dfs된 coin 개수가 일치하는지 확인
- dfs_recur : 출구 및 벽일 경우 재귀 탈출조건 세우고 상하좌우 재귀
- file_name : .ber 파일이 들어오는지 확인
#include "so_long.h"
int dfs(t_param *p)
{
t_list *cur;
char *line;
char **visit;
cur = p->map;
p->y = 0;
visit = (char **)malloc(sizeof(char *) * (p->count + 1));
while (cur)
{
p->x = 0;
line = cur->data;
visit[p->y] = (char *)malloc(sizeof(char) * (ft_strlen(line) + 1));
while (*line)
visit[p->y][p->x++] = *line++;
p->y++;
cur = cur->next;
}
dfs_recur(p, p->play_x, p->play_y, visit);
if (p->coin != p->coin_check)
{
ft_printf_s("Error : Invalid Map Route\n");
return (1);
}
free(visit);
return (0);
}
int dfs_recur(t_param *p, int x, int y, char **visit)
{
if (x < 0 || x >= p->win_x || y < 0 || y >= p->win_y)
return (0);
if (visit[y][x] == '1')
return (0);
else if (visit[y][x] == 'E')
return (0);
else if (visit[y][x] != 'V')
{
if (visit[y][x] == 'C')
p->coin_check++;
visit[y][x] = 'V';
dfs_recur(p, x - 1, y, visit);
dfs_recur(p, x + 1, y, visit);
dfs_recur(p, x, y - 1, visit);
dfs_recur(p, x, y + 1, visit);
return (1);
}
return (0);
}
int file_name(char *file)
{
int i;
char *ext;
i = 0;
ext = ".ber";
while (*(file + i) != '.')
i++;
while (*(file + i) || *ext)
{
if (*(file + i) != *ext)
return (1);
ext++;
i++;
}
return (0);
}
void ft_printf_s(char *s)
{
size_t i;
char c;
i = 0;
if (!s)
{
if (write(1, "(null)", 6) == -1)
return ;
}
while (*(s + i))
{
c = *(s + i);
write(1, &c, 1);
i++;
}
}
void ft_printf_d(long long n)
{
int i;
char c[12];
i = 0;
if (n < 0)
{
c[0] = '-';
write(1, &c, 1);
n *= -1;
}
while (n >= 10)
{
c[i] = (n % 10) + 48;
n /= 10;
i++;
}
c[i] = (n % 10) + 48;
while (i >= 0)
{
write(1, &c[i], 1);
i--;
}
c[0] = '\n';
write(1, &c, 1);
}
📌 so_long.h
#ifndef SO_LONG_H
# define SO_LONG_H
# define W 13
# define A 0
# define S 1
# define D 2
# define ESC 53
# define RED 17
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <fcntl.h>
# include "mlx/mlx.h"
# include "libft/get_next_line.h"
typedef struct s_list
{
char *data;
struct s_list *next;
} t_list;
typedef struct s_param
{
void *mlx;
void *win;
void *img_mario;
void *img_map;
void *img_coin;
void *img_wall;
void *img_exit;
t_list *map;
int win_x;
int win_y;
int x;
int y;
int play_x;
int play_y;
int move;
int count;
int coin;
int coin_check;
int exit;
int player;
} t_param;
void ft_printf_s(char *s);
void ft_printf_d(long long n);
int file_name(char *file);
int init(t_param *p);
void init_player(t_param *p);
int move(int key, t_param *p);
void move_up(t_param *p);
void move_left(t_param *p);
void move_down(t_param *p);
void move_right(t_param *p);
int map_read(t_param *p, char *file);
int map_strjoin(t_param *p, char *line);
void map_set(t_param *p);
void map_imgs(t_param *p, char c);
void map_free(t_param *p);
int map_exit(t_param *p);
int map_red_exit(t_param *p);
int map_check(t_param *p);
int map_check_line(t_param *p);
int map_check_wall(t_param *p, int end);
int map_check_count(t_param *p);
int check_wall(char *str, int count, int end);
int dfs(t_param *p);
int dfs_recur(t_param *p, int x, int y, char **visit);
#endif
📌 Makefile
NAME = so_long
SRCS = libft/get_next_line.c\
libft/get_next_line_utils.c\
so_long.c\
so_long_move.c\
so_long_map.c\
so_long_check.c\
so_long_dfs.c
OBJS = $(SRCS:.c=.o)
%.o:%.c so_long.h
cc -Wall -Wextra -Werror -c $< -o $@
$(NAME) : $(OBJS)
make -C ./mlx/
cc -o so_long $(OBJS) -L./mlx -lmlx -framework OpenGL -framework AppKit
all : $(NAME)
clean :
make -C ./mlx clean
rm -rf $(OBJS)
fclean : clean
rm -rf $(NAME)
re :
make fclean
make all
.PHONY : all clean fclean re
728x90
반응형