Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Author: Usein Abilev (the.usein@gmail.com)
- // Updated: 16.12.2021
- // Created: 20.11.2021
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- // определяем основные макросы/константы
- #define MIN_ROWS_NUMBER 3
- #define MAX_ROWS_NUMBER 100
- #define MIN_COLUMNS_NUMBER 3
- #define MAX_COLUMNS_NUMBER 100
- #define PLAYER_INITIAL_ROW_POSITION 0
- #define PLAYER_INITIAL_COLUMN_POSITION 0
- #define MAP_ISLAND_SYMBOL 0x23
- #define MAP_VOID_SYMBOL 0x2e
- // Определяем битовую последовательность для направлений
- enum direction {
- TOP = 1 << 0,
- BOTTOM = 1 << 1,
- LEFT = 1 << 2,
- RIGHT = 1 << 3
- };
- // Определяем числовой тип для клеток острова
- enum map_cell_type {
- MAP_CELL_VOID = -127,
- MAP_CELL_ISLAND,
- MAP_CELL_MARKED,
- MAP_CELL_ADJACENT,
- MAP_CELL_UNKNOWN
- };
- // Main board structure
- typedef struct {
- size_t rows;
- size_t columns;
- char** map;
- } board_t; // 24 bytes
- typedef struct {
- size_t row;
- size_t column;
- int current_direction;
- int expected_direction;
- bool dead_end;
- bool started;
- } player_t; // 32 bytes
- // Определения функций
- // General declarations
- void scan_input(char*** map, size_t* rows, size_t* columns);
- char** scan_map(size_t rows, size_t columns);
- // Board declaration
- board_t* board_init(char** map, size_t rows, size_t columns);
- int board_check(board_t* board);
- char board_get_item(board_t* board, size_t row, size_t column);
- char board_get_item_by_curdir(board_t* board, player_t* player);
- void board_map_normalize(board_t* board);
- void board_print(board_t* board);
- void board_fill_adjacent_cells(board_t* board);
- int board_is_adjacent_cell(board_t* board, size_t row, size_t column);
- void board_clear(board_t* board);
- // Player declartion
- player_t* player_init(size_t row, size_t column);
- void player_run(player_t* player, board_t* board);
- void player_move(player_t* player);
- void player_calc_next(player_t* player, board_t* board);
- int player_get_adjacent_direction_idx(player_t* player, board_t* board);
- int board_is_adjacent_cell(board_t* board, size_t row, size_t column);
- int player_get_adjacent_direction(player_t* player, board_t* board, bool marked);
- // Utils declaration
- int get_expected_direction(int direction);
- void panic(const char* const message);
- int check_number_range(int n, int min, int max);
- // Основной алгоритм начинается здесь.
- // program entry point
- int main(int argc, char** argv) {
- size_t rows;
- size_t columns;
- char** map;
- scan_input(&map, &rows, &columns);
- board_t* board = board_init(map, rows, columns);
- board_print(board);
- board_check(board);
- player_t* player = player_init(PLAYER_INITIAL_ROW_POSITION, PLAYER_INITIAL_COLUMN_POSITION);
- player_run(player, board);
- board_clear(board);
- return EXIT_SUCCESS;
- }
- // General defintions
- /* Читаем и проверяем входные данные для кол-ва строк, колонок */
- void scan_input(char*** map, size_t* rows, size_t* columns) {
- printf("Enter the rows: ");
- scanf("%li", rows);
- if (!check_number_range(*rows, MIN_ROWS_NUMBER, MAX_ROWS_NUMBER)) {
- panic("Invalid rows number. The number must have range from 3 to 100");
- }
- printf("Enter the columns: ");
- scanf("%li", columns);
- if (!check_number_range(*columns, MIN_COLUMNS_NUMBER, MAX_COLUMNS_NUMBER)) {
- panic("Invalid columns number. The number must have range from 3 to 100");
- }
- // Map input & check
- *map = scan_map(*rows, *columns);
- }
- // Читаем и проверяем рисунок карты
- char** scan_map(size_t rows, size_t columns) {
- char** buffer = malloc(sizeof(char*) * rows);
- printf("Enter the island map: (%lix%li)\n", rows, columns);
- for (size_t current_row = 0; current_row < rows; current_row++) {
- printf("[%li] > ", current_row);
- char* line = malloc(sizeof(char) * columns + 1);
- size_t current_column = 0;
- while (current_column + 1 <= columns) {
- char c = getchar();
- if (c == '\r' || c == '\n') {
- continue;
- }
- line[current_column] = c;
- current_column++;
- }
- int length = strlen(line);
- if (length != columns) {
- panic("Unexpected line terminate.");
- }
- for (size_t i = 0; i < columns; i++) {
- if (line[i] != MAP_VOID_SYMBOL && line[i] != MAP_ISLAND_SYMBOL) {
- panic("Invalid map symbol. Available symbols: [.#]");
- break;
- }
- }
- buffer[current_row] = line;
- }
- return buffer;
- }
- // Board defintions
- board_t* board_init(char** map, size_t rows, size_t columns) {
- board_t* board = malloc(sizeof(board_t));
- board->map = map;
- board->rows = rows;
- board->columns = columns;
- board_map_normalize(board);
- board_fill_adjacent_cells(board);
- return board;
- }
- // Нормализуем карту, приводим все символы в их числовые аналоги
- void board_map_normalize(board_t* board) {
- for (int i = 0; i < board->rows; i++) {
- for (int j = 0; j < board->columns; j++) {
- if (board->map[i][j] == MAP_ISLAND_SYMBOL)
- board->map[i][j] = MAP_CELL_ISLAND;
- if (board->map[i][j] == MAP_VOID_SYMBOL)
- board->map[i][j] = MAP_CELL_VOID;
- }
- }
- }
- // Проверяем карту: оступ от краев, пустые места на острове
- int board_check(board_t* board) {
- char* const incorrect = "Incorrect map. Island must've the padding";
- for (int column = 0; column < board->columns; column++) {
- if (board->map[0][column] == MAP_CELL_ISLAND || board->map[board->rows - 1][column] == MAP_CELL_ISLAND) {
- panic(incorrect);
- }
- }
- for (int row = 0; row < board->rows; row++) {
- if (board->map[row][0] == MAP_CELL_ISLAND || board->map[row][board->columns - 1] == MAP_CELL_ISLAND) {
- panic(incorrect);
- }
- }
- int has_island_part = 0;
- for (int row = 0; row < board->rows; row++) {
- for (int column = 0; column < board->columns; column++) {
- if (board->map[row][column] == MAP_CELL_ISLAND) {
- has_island_part = 1;
- int has_neighbor = 0;
- int vertical = board->map[row - 1][column] == MAP_CELL_ISLAND || board->map[row + 1][column] == MAP_CELL_ISLAND;
- int horizontal = board->map[row][column - 1] == MAP_CELL_ISLAND || board->map[row][column + 1] == MAP_CELL_ISLAND;
- if (vertical || horizontal) has_neighbor = 1;
- if (!has_neighbor) panic("Incorrect island map. The island must have 1 adjacent cell at the minimum");
- }
- }
- }
- if (!has_island_part) {
- panic("Incorrect island map. The island not found.");
- }
- }
- // Заполняем соседние клетки с островом
- void board_fill_adjacent_cells(board_t* board) {
- for (int i = 0; i < board->rows; i++) {
- for (int j = 0; j < board->columns; j++) {
- if (board->map[i][j] == MAP_CELL_ISLAND) continue;
- int adjacent = board_is_adjacent_cell(board, i, j);
- if (adjacent) {
- board->map[i][j] = MAP_CELL_ADJACENT;
- }
- }
- }
- }
- // Является ли клетка [row][column] соседней с островом
- int board_is_adjacent_cell(board_t* board, size_t row, size_t column) {
- return (board_get_item(board, row - 1, column) == MAP_CELL_ISLAND ||
- board_get_item(board, row + 1, column) == MAP_CELL_ISLAND ||
- board_get_item(board, row, column - 1) == MAP_CELL_ISLAND ||
- board_get_item(board, row, column + 1) == MAP_CELL_ISLAND ||
- board_get_item(board, row - 1, column + 1) == MAP_CELL_ISLAND ||
- board_get_item(board, row - 1, column - 1) == MAP_CELL_ISLAND ||
- board_get_item(board, row + 1, column + 1) == MAP_CELL_ISLAND ||
- board_get_item(board, row + 1, column - 1) == MAP_CELL_ISLAND);
- }
- // получаем содержимое клетки по текущему направлению самолета
- char board_get_item_by_curdir(board_t* board, player_t* player) {
- if (player->current_direction & TOP) return board_get_item(board, player->row - 1, player->column);
- if (player->current_direction & BOTTOM) return board_get_item(board, player->row + 1, player->column);
- if (player->current_direction & LEFT) return board_get_item(board, player->row, player->column - 1);
- if (player->current_direction & RIGHT) return board_get_item(board, player->row, player->column + 1);
- }
- // Получаем клетку по индексу строки и столбца, проверяя выход за границы массива
- char board_get_item(board_t* board, size_t row, size_t column) {
- if (row < 0 || column < 0) return MAP_CELL_UNKNOWN;
- if (row > board->rows - 1 || column > board->columns - 1) return MAP_CELL_UNKNOWN;
- return board->map[row][column];
- }
- // Выводим рисунок карты на выходной поток
- void board_print(board_t* board) {
- printf("\n== The Island Map ==\n");
- for (int i = 0; i < board->rows; i++) {
- for (int j = 0; j < board->columns; j++) {
- char c = board->map[i][j];
- if (c == MAP_CELL_ISLAND) printf("#");
- if (c == MAP_CELL_VOID) printf(".");
- if (c == MAP_CELL_ADJACENT) printf("*");
- if (c == MAP_CELL_MARKED) printf("-");
- }
- printf("\n");
- }
- }
- // Освобождаем память всей структуры board
- void board_clear(board_t* board) {
- for (size_t row = 0; row < board->rows; row++) {
- free(board->map[row]);
- board->map[row] = NULL;
- }
- free(board->map);
- free(board);
- board = NULL;
- }
- // Player definitions
- // Инициализация игрока (самолета)
- player_t* player_init(size_t row, size_t column) {
- player_t* player = malloc(sizeof(player_t));
- player->row = row;
- player->column = column;
- player->expected_direction = RIGHT;
- player->dead_end = false;
- player->started = true;
- return player;
- }
- // Основной цикл движения самолета по карте
- void player_run(player_t* player, board_t* board) {
- while (player->started) {
- board->map[player->row][player->column] = MAP_CELL_MARKED;
- player_calc_next(player, board);
- player_move(player);
- printf("plane position: [%li, %li]\n\n", player->column, player->row);
- if (player->started && player->row == PLAYER_INITIAL_ROW_POSITION && player->column == PLAYER_INITIAL_COLUMN_POSITION) {
- player->started = false;
- }
- }
- }
- // Вычисление следующего шага самолета
- void player_calc_next(player_t* player, board_t* board) {
- int adjacent = player_get_adjacent_direction(player, board, 0);
- if (adjacent != 0) {
- player->dead_end = false;
- if (adjacent & player->expected_direction) {
- player->current_direction = player->expected_direction;
- player->expected_direction = get_expected_direction(player->current_direction);
- } else {
- // BUG: infinite loop
- while (board_get_item_by_curdir(board, player) != MAP_CELL_ADJACENT) {
- player->current_direction = get_expected_direction(player->current_direction);
- player->expected_direction = get_expected_direction(player->expected_direction);
- continue;
- }
- }
- } else {
- int adjacent_marked = player_get_adjacent_direction(player, board, 1);
- if (!player->dead_end) {
- player->dead_end = true;
- player->current_direction = adjacent_marked;
- player->expected_direction = get_expected_direction(adjacent_marked);
- }
- if (adjacent_marked & player->expected_direction) {
- player->current_direction = player->expected_direction;
- player->expected_direction = get_expected_direction(player->current_direction);
- }
- }
- }
- int player_get_adjacent_direction_idx(player_t* player, board_t* board) {
- int max = 0;
- int maxdirection;
- int count;
- if (player->current_direction & TOP && (count = board_is_adjacent_cell(board, player->row - 1, player->column)) > max) {
- max = count;
- maxdirection = TOP;
- }
- if (player->current_direction & BOTTOM && (count = board_is_adjacent_cell(board, player->row + 1, player->column)) > max) {
- max = count;
- maxdirection = BOTTOM;
- }
- if (player->current_direction & RIGHT && (count = board_is_adjacent_cell(board, player->row, player->column + 1)) > max) {
- max = count;
- maxdirection = RIGHT;
- }
- if (player->current_direction & LEFT && (count = board_is_adjacent_cell(board, player->row, player->column - 1)) > max) {
- max = count;
- maxdirection = LEFT;
- }
- return maxdirection;
- }
- // Получить направление сосденей клетки с островом
- // player - структура игрока (самолета)
- // board - структура карты
- // marked - флаг, какой тип клеток мы смотрим. Используется для выхода из тупиков (заливов)
- int player_get_adjacent_direction(player_t* player, board_t* board, bool marked) {
- char bottom = board_get_item(board, player->row + 1, player->column);
- char top = board_get_item(board, player->row - 1, player->column);
- char left = board_get_item(board, player->row, player->column - 1);
- char right = board_get_item(board, player->row, player->column + 1);
- int flag = 0;
- if (marked) {
- if (bottom == MAP_CELL_MARKED) flag |= BOTTOM;
- if (top == MAP_CELL_MARKED) flag |= TOP;
- if (left == MAP_CELL_MARKED) flag |= LEFT;
- if (right == MAP_CELL_MARKED) flag |= RIGHT;
- return flag;
- }
- if (bottom == MAP_CELL_ADJACENT || bottom > 0) flag |= BOTTOM;
- if (top == MAP_CELL_ADJACENT || top > 0) flag |= TOP;
- if (left == MAP_CELL_ADJACENT || left > 0) flag |= LEFT;
- if (right == MAP_CELL_ADJACENT || right > 0) flag |= RIGHT;
- return flag;
- }
- // Получить направление с правой стороны относительно аргумента direction
- int get_expected_direction(int direction) {
- if (direction & BOTTOM) return LEFT;
- if (direction & TOP) return RIGHT;
- if (direction & LEFT) return TOP;
- if (direction & RIGHT) return BOTTOM;
- return 0;
- }
- // Двигаем игрока в текущее его направление
- void player_move(player_t* player) {
- int direction = player->current_direction;
- if (direction & TOP)
- player->row--;
- else if (direction & BOTTOM)
- player->row++;
- else if (direction & RIGHT)
- player->column++;
- else if (direction & LEFT)
- player->column--;
- }
- // Utils defintions
- int check_number_range(int n, int min, int max) {
- return (n >= min && n <= max);
- }
- // Функция для отображения ошибки в выходной поток
- void panic(const char* const message) {
- fprintf(stderr, "[\033[0;31merror\033[0m]: %s\n", message);
- exit(EXIT_FAILURE);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement