Advertisement
the_usik

C Island flyby (update)

Dec 16th, 2021
715
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 15.72 KB | None | 0 0
  1. // Author: Usein Abilev (the.usein@gmail.com)
  2. // Updated: 16.12.2021
  3. // Created: 20.11.2021
  4.  
  5. #include <stdbool.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9.  
  10. // определяем основные макросы/константы
  11. #define MIN_ROWS_NUMBER 3
  12. #define MAX_ROWS_NUMBER 100
  13.  
  14. #define MIN_COLUMNS_NUMBER 3
  15. #define MAX_COLUMNS_NUMBER 100
  16.  
  17. #define PLAYER_INITIAL_ROW_POSITION 0
  18. #define PLAYER_INITIAL_COLUMN_POSITION 0
  19.  
  20. #define MAP_ISLAND_SYMBOL 0x23
  21. #define MAP_VOID_SYMBOL 0x2e
  22.  
  23. // Определяем битовую последовательность для направлений
  24. enum direction {
  25.     TOP = 1 << 0,
  26.     BOTTOM = 1 << 1,
  27.     LEFT = 1 << 2,
  28.     RIGHT = 1 << 3
  29. };
  30.  
  31. // Определяем числовой тип для клеток острова
  32. enum map_cell_type {
  33.     MAP_CELL_VOID = -127,
  34.     MAP_CELL_ISLAND,
  35.     MAP_CELL_MARKED,
  36.     MAP_CELL_ADJACENT,
  37.     MAP_CELL_UNKNOWN
  38. };
  39.  
  40. // Main board structure
  41. typedef struct {
  42.     size_t rows;
  43.     size_t columns;
  44.     char** map;
  45. } board_t;  // 24 bytes
  46.  
  47. typedef struct {
  48.     size_t row;
  49.     size_t column;
  50.     int current_direction;
  51.     int expected_direction;
  52.     bool dead_end;
  53.     bool started;
  54. } player_t;  // 32 bytes
  55.  
  56. // Определения функций
  57. // General declarations
  58. void scan_input(char*** map, size_t* rows, size_t* columns);
  59. char** scan_map(size_t rows, size_t columns);
  60.  
  61. // Board declaration
  62. board_t* board_init(char** map, size_t rows, size_t columns);
  63. int board_check(board_t* board);
  64. char board_get_item(board_t* board, size_t row, size_t column);
  65. char board_get_item_by_curdir(board_t* board, player_t* player);
  66. void board_map_normalize(board_t* board);
  67. void board_print(board_t* board);
  68. void board_fill_adjacent_cells(board_t* board);
  69. int board_is_adjacent_cell(board_t* board, size_t row, size_t column);
  70. void board_clear(board_t* board);
  71.  
  72. // Player declartion
  73. player_t* player_init(size_t row, size_t column);
  74. void player_run(player_t* player, board_t* board);
  75. void player_move(player_t* player);
  76. void player_calc_next(player_t* player, board_t* board);
  77. int player_get_adjacent_direction_idx(player_t* player, board_t* board);
  78. int board_is_adjacent_cell(board_t* board, size_t row, size_t column);
  79. int player_get_adjacent_direction(player_t* player, board_t* board, bool marked);
  80.  
  81. // Utils declaration
  82. int get_expected_direction(int direction);
  83. void panic(const char* const message);
  84. int check_number_range(int n, int min, int max);
  85.  
  86. // Основной алгоритм начинается здесь.
  87. // program entry point
  88. int main(int argc, char** argv) {
  89.     size_t rows;
  90.     size_t columns;
  91.     char** map;
  92.  
  93.     scan_input(&map, &rows, &columns);
  94.     board_t* board = board_init(map, rows, columns);
  95.     board_print(board);
  96.     board_check(board);
  97.  
  98.     player_t* player = player_init(PLAYER_INITIAL_ROW_POSITION, PLAYER_INITIAL_COLUMN_POSITION);
  99.     player_run(player, board);
  100.     board_clear(board);
  101.  
  102.     return EXIT_SUCCESS;
  103. }
  104.  
  105. // General defintions
  106.  
  107. /* Читаем и проверяем входные данные для кол-ва строк, колонок */
  108. void scan_input(char*** map, size_t* rows, size_t* columns) {
  109.     printf("Enter the rows: ");
  110.     scanf("%li", rows);
  111.     if (!check_number_range(*rows, MIN_ROWS_NUMBER, MAX_ROWS_NUMBER)) {
  112.         panic("Invalid rows number. The number must have range from 3 to 100");
  113.     }
  114.  
  115.     printf("Enter the columns: ");
  116.     scanf("%li", columns);
  117.     if (!check_number_range(*columns, MIN_COLUMNS_NUMBER, MAX_COLUMNS_NUMBER)) {
  118.         panic("Invalid columns number. The number must have range from 3 to 100");
  119.     }
  120.  
  121.     // Map input & check
  122.     *map = scan_map(*rows, *columns);
  123. }
  124.  
  125. // Читаем и проверяем рисунок карты
  126. char** scan_map(size_t rows, size_t columns) {
  127.     char** buffer = malloc(sizeof(char*) * rows);
  128.  
  129.     printf("Enter the island map: (%lix%li)\n", rows, columns);
  130.  
  131.     for (size_t current_row = 0; current_row < rows; current_row++) {
  132.         printf("[%li] > ", current_row);
  133.         char* line = malloc(sizeof(char) * columns + 1);
  134.         size_t current_column = 0;
  135.         while (current_column + 1 <= columns) {
  136.             char c = getchar();
  137.             if (c == '\r' || c == '\n') {
  138.                 continue;
  139.             }
  140.  
  141.             line[current_column] = c;
  142.             current_column++;
  143.         }
  144.  
  145.         int length = strlen(line);
  146.  
  147.         if (length != columns) {
  148.             panic("Unexpected line terminate.");
  149.         }
  150.  
  151.         for (size_t i = 0; i < columns; i++) {
  152.             if (line[i] != MAP_VOID_SYMBOL && line[i] != MAP_ISLAND_SYMBOL) {
  153.                 panic("Invalid map symbol. Available symbols: [.#]");
  154.                 break;
  155.             }
  156.         }
  157.  
  158.         buffer[current_row] = line;
  159.     }
  160.  
  161.     return buffer;
  162. }
  163.  
  164. // Board defintions
  165. board_t* board_init(char** map, size_t rows, size_t columns) {
  166.     board_t* board = malloc(sizeof(board_t));
  167.     board->map = map;
  168.     board->rows = rows;
  169.     board->columns = columns;
  170.     board_map_normalize(board);
  171.     board_fill_adjacent_cells(board);
  172.  
  173.     return board;
  174. }
  175.  
  176. // Нормализуем карту, приводим все символы в их числовые аналоги
  177. void board_map_normalize(board_t* board) {
  178.     for (int i = 0; i < board->rows; i++) {
  179.         for (int j = 0; j < board->columns; j++) {
  180.             if (board->map[i][j] == MAP_ISLAND_SYMBOL)
  181.                 board->map[i][j] = MAP_CELL_ISLAND;
  182.             if (board->map[i][j] == MAP_VOID_SYMBOL)
  183.                 board->map[i][j] = MAP_CELL_VOID;
  184.         }
  185.     }
  186. }
  187.  
  188. // Проверяем карту: оступ от краев, пустые места на острове
  189. int board_check(board_t* board) {
  190.     char* const incorrect = "Incorrect map. Island must've the padding";
  191.  
  192.     for (int column = 0; column < board->columns; column++) {
  193.         if (board->map[0][column] == MAP_CELL_ISLAND || board->map[board->rows - 1][column] == MAP_CELL_ISLAND) {
  194.             panic(incorrect);
  195.         }
  196.     }
  197.  
  198.     for (int row = 0; row < board->rows; row++) {
  199.         if (board->map[row][0] == MAP_CELL_ISLAND || board->map[row][board->columns - 1] == MAP_CELL_ISLAND) {
  200.             panic(incorrect);
  201.         }
  202.     }
  203.  
  204.     int has_island_part = 0;
  205.     for (int row = 0; row < board->rows; row++) {
  206.         for (int column = 0; column < board->columns; column++) {
  207.             if (board->map[row][column] == MAP_CELL_ISLAND) {
  208.                 has_island_part = 1;
  209.                 int has_neighbor = 0;
  210.                 int vertical = board->map[row - 1][column] == MAP_CELL_ISLAND || board->map[row + 1][column] == MAP_CELL_ISLAND;
  211.                 int horizontal = board->map[row][column - 1] == MAP_CELL_ISLAND || board->map[row][column + 1] == MAP_CELL_ISLAND;
  212.                 if (vertical || horizontal) has_neighbor = 1;
  213.                 if (!has_neighbor) panic("Incorrect island map. The island must have 1 adjacent cell at the minimum");
  214.             }
  215.         }
  216.     }
  217.  
  218.     if (!has_island_part) {
  219.         panic("Incorrect island map. The island not found.");
  220.     }
  221. }
  222.  
  223. // Заполняем соседние клетки с островом
  224. void board_fill_adjacent_cells(board_t* board) {
  225.     for (int i = 0; i < board->rows; i++) {
  226.         for (int j = 0; j < board->columns; j++) {
  227.             if (board->map[i][j] == MAP_CELL_ISLAND) continue;
  228.             int adjacent = board_is_adjacent_cell(board, i, j);
  229.             if (adjacent) {
  230.                 board->map[i][j] = MAP_CELL_ADJACENT;
  231.             }
  232.         }
  233.     }
  234. }
  235.  
  236. // Является ли клетка [row][column] соседней с островом
  237. int board_is_adjacent_cell(board_t* board, size_t row, size_t column) {
  238.     return (board_get_item(board, row - 1, column) == MAP_CELL_ISLAND ||
  239.             board_get_item(board, row + 1, column) == MAP_CELL_ISLAND ||
  240.             board_get_item(board, row, column - 1) == MAP_CELL_ISLAND ||
  241.             board_get_item(board, row, column + 1) == MAP_CELL_ISLAND ||
  242.             board_get_item(board, row - 1, column + 1) == MAP_CELL_ISLAND ||
  243.             board_get_item(board, row - 1, column - 1) == MAP_CELL_ISLAND ||
  244.             board_get_item(board, row + 1, column + 1) == MAP_CELL_ISLAND ||
  245.             board_get_item(board, row + 1, column - 1) == MAP_CELL_ISLAND);
  246. }
  247.  
  248. // получаем содержимое клетки по текущему направлению самолета
  249. char board_get_item_by_curdir(board_t* board, player_t* player) {
  250.     if (player->current_direction & TOP) return board_get_item(board, player->row - 1, player->column);
  251.     if (player->current_direction & BOTTOM) return board_get_item(board, player->row + 1, player->column);
  252.     if (player->current_direction & LEFT) return board_get_item(board, player->row, player->column - 1);
  253.     if (player->current_direction & RIGHT) return board_get_item(board, player->row, player->column + 1);
  254. }
  255.  
  256. // Получаем клетку по индексу строки и столбца, проверяя выход за границы массива
  257. char board_get_item(board_t* board, size_t row, size_t column) {
  258.     if (row < 0 || column < 0) return MAP_CELL_UNKNOWN;
  259.     if (row > board->rows - 1 || column > board->columns - 1) return MAP_CELL_UNKNOWN;
  260.     return board->map[row][column];
  261. }
  262.  
  263. // Выводим рисунок карты на выходной поток
  264. void board_print(board_t* board) {
  265.     printf("\n== The Island Map ==\n");
  266.     for (int i = 0; i < board->rows; i++) {
  267.         for (int j = 0; j < board->columns; j++) {
  268.             char c = board->map[i][j];
  269.             if (c == MAP_CELL_ISLAND) printf("#");
  270.             if (c == MAP_CELL_VOID) printf(".");
  271.             if (c == MAP_CELL_ADJACENT) printf("*");
  272.             if (c == MAP_CELL_MARKED) printf("-");
  273.         }
  274.  
  275.         printf("\n");
  276.     }
  277. }
  278.  
  279. // Освобождаем память всей структуры board
  280. void board_clear(board_t* board) {
  281.     for (size_t row = 0; row < board->rows; row++) {
  282.         free(board->map[row]);
  283.         board->map[row] = NULL;
  284.     }
  285.  
  286.     free(board->map);
  287.     free(board);
  288.     board = NULL;
  289. }
  290.  
  291. // Player definitions
  292.  
  293. // Инициализация игрока (самолета)
  294. player_t* player_init(size_t row, size_t column) {
  295.     player_t* player = malloc(sizeof(player_t));
  296.     player->row = row;
  297.     player->column = column;
  298.     player->expected_direction = RIGHT;
  299.     player->dead_end = false;
  300.     player->started = true;
  301.  
  302.     return player;
  303. }
  304.  
  305. // Основной цикл движения самолета по карте
  306. void player_run(player_t* player, board_t* board) {
  307.     while (player->started) {
  308.         board->map[player->row][player->column] = MAP_CELL_MARKED;
  309.         player_calc_next(player, board);
  310.         player_move(player);
  311.         printf("plane position: [%li, %li]\n\n", player->column, player->row);
  312.  
  313.         if (player->started && player->row == PLAYER_INITIAL_ROW_POSITION && player->column == PLAYER_INITIAL_COLUMN_POSITION) {
  314.             player->started = false;
  315.         }
  316.     }
  317. }
  318.  
  319. // Вычисление следующего шага самолета
  320. void player_calc_next(player_t* player, board_t* board) {
  321.     int adjacent = player_get_adjacent_direction(player, board, 0);
  322.  
  323.     if (adjacent != 0) {
  324.         player->dead_end = false;
  325.         if (adjacent & player->expected_direction) {
  326.             player->current_direction = player->expected_direction;
  327.             player->expected_direction = get_expected_direction(player->current_direction);
  328.         } else {
  329.             // BUG: infinite loop
  330.             while (board_get_item_by_curdir(board, player) != MAP_CELL_ADJACENT) {
  331.                 player->current_direction = get_expected_direction(player->current_direction);
  332.                 player->expected_direction = get_expected_direction(player->expected_direction);
  333.                 continue;
  334.             }
  335.         }
  336.     } else {
  337.         int adjacent_marked = player_get_adjacent_direction(player, board, 1);
  338.         if (!player->dead_end) {
  339.             player->dead_end = true;
  340.             player->current_direction = adjacent_marked;
  341.             player->expected_direction = get_expected_direction(adjacent_marked);
  342.         }
  343.  
  344.         if (adjacent_marked & player->expected_direction) {
  345.             player->current_direction = player->expected_direction;
  346.             player->expected_direction = get_expected_direction(player->current_direction);
  347.         }
  348.     }
  349. }
  350.  
  351. int player_get_adjacent_direction_idx(player_t* player, board_t* board) {
  352.     int max = 0;
  353.     int maxdirection;
  354.     int count;
  355.  
  356.     if (player->current_direction & TOP && (count = board_is_adjacent_cell(board, player->row - 1, player->column)) > max) {
  357.         max = count;
  358.         maxdirection = TOP;
  359.     }
  360.  
  361.     if (player->current_direction & BOTTOM && (count = board_is_adjacent_cell(board, player->row + 1, player->column)) > max) {
  362.         max = count;
  363.         maxdirection = BOTTOM;
  364.     }
  365.  
  366.     if (player->current_direction & RIGHT && (count = board_is_adjacent_cell(board, player->row, player->column + 1)) > max) {
  367.         max = count;
  368.         maxdirection = RIGHT;
  369.     }
  370.  
  371.     if (player->current_direction & LEFT && (count = board_is_adjacent_cell(board, player->row, player->column - 1)) > max) {
  372.         max = count;
  373.         maxdirection = LEFT;
  374.     }
  375.  
  376.     return maxdirection;
  377. }
  378.  
  379. // Получить направление сосденей клетки с островом
  380. // player - структура игрока (самолета)
  381. // board - структура карты
  382. // marked - флаг, какой тип клеток мы смотрим. Используется для выхода из тупиков (заливов)
  383. int player_get_adjacent_direction(player_t* player, board_t* board, bool marked) {
  384.     char bottom = board_get_item(board, player->row + 1, player->column);
  385.     char top = board_get_item(board, player->row - 1, player->column);
  386.     char left = board_get_item(board, player->row, player->column - 1);
  387.     char right = board_get_item(board, player->row, player->column + 1);
  388.  
  389.     int flag = 0;
  390.     if (marked) {
  391.         if (bottom == MAP_CELL_MARKED) flag |= BOTTOM;
  392.         if (top == MAP_CELL_MARKED) flag |= TOP;
  393.         if (left == MAP_CELL_MARKED) flag |= LEFT;
  394.         if (right == MAP_CELL_MARKED) flag |= RIGHT;
  395.         return flag;
  396.     }
  397.  
  398.     if (bottom == MAP_CELL_ADJACENT || bottom > 0) flag |= BOTTOM;
  399.     if (top == MAP_CELL_ADJACENT || top > 0) flag |= TOP;
  400.     if (left == MAP_CELL_ADJACENT || left > 0) flag |= LEFT;
  401.     if (right == MAP_CELL_ADJACENT || right > 0) flag |= RIGHT;
  402.     return flag;
  403. }
  404.  
  405. // Получить направление с правой стороны относительно аргумента direction
  406. int get_expected_direction(int direction) {
  407.     if (direction & BOTTOM) return LEFT;
  408.     if (direction & TOP) return RIGHT;
  409.     if (direction & LEFT) return TOP;
  410.     if (direction & RIGHT) return BOTTOM;
  411.  
  412.     return 0;
  413. }
  414.  
  415. // Двигаем игрока в текущее его направление
  416. void player_move(player_t* player) {
  417.     int direction = player->current_direction;
  418.     if (direction & TOP)
  419.         player->row--;
  420.     else if (direction & BOTTOM)
  421.         player->row++;
  422.     else if (direction & RIGHT)
  423.         player->column++;
  424.     else if (direction & LEFT)
  425.         player->column--;
  426. }
  427.  
  428. // Utils defintions
  429. int check_number_range(int n, int min, int max) {
  430.     return (n >= min && n <= max);
  431. }
  432.  
  433. // Функция для отображения ошибки в выходной поток
  434. void panic(const char* const message) {
  435.     fprintf(stderr, "[\033[0;31merror\033[0m]: %s\n", message);
  436.     exit(EXIT_FAILURE);
  437. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement