The full description can be found in MountainClimbers.zip.
You are going to write a program that opens the file input.txt and finds the shortest path from the starting position to the end position. The path is constrained by the elevation of the surrounding area.
The contour map is broken into a grid; the elevation of each square on the grid is given by a single lowercase letter, where a is the lowest elevation and z is the highest elevation.
Also included in the height map are your current position (S) and the target position for the best view (E). Your current position has elevation a and the target position has elevation z.
You would like to reach E, but to save energy for the rest of the trip home, want to do it in as few steps as possible. You can move exactly one square up, down, left, or right during each step. Because a certain MIDN forgot their climbing gear at home, the elevation of the destination square can be at most one higher than the elevation of the current square. Meaning if you are at an elevation of height m, you can step to n but not to o. This also means that the elevation of the destination square can be much lower than that of your current square.
You have two parts to this challenge:
Find the fewest number of steps to move from starting position S to the target location.
Find the fewest number of steps to move from any base position a to the target location.
Concatenate these answers in flag braces separated by a semicolon (flag{part1:part2}).
Walkthrough
This challenging problem is aptly worth the points it is worth. This challenge tests your ability to implement a BFS algorithm with proper backtracking to find the shortest path.
We are using a DFS to solve this problem. Let's talk about data structures:
I chose to store the current length as an int. Realistically, I could have used an unsigned integer, but with BFS/DFS algorithms, I like to have a sanity check that the number won't underflow.
We need to store the grid map. I chose a 2D static array for this since the size is unchanging. I ended up making the variable global so any function can access it.
At each state, we need to store the current queue of positions. I used a linked list to hold this.
We also need to store the visited positions. I used a linked list for this as well.
For the linked list, we need to know the position and the least number of steps to get to that position. I made a struct to hold this information.
typedefstruct node {int x, y, steps;struct node *next;} node;
We'll need to keep a global linked list containing the queue of nodes plus the visited nodes.
node *queue,*visited;
We also want to know the height and width of the board. We'll store this globally too so every function can see it.
int height, width;
With this out of the way, we can open the file and initialize data. We'll open the file as normal.
// Open file for readingFILE *file =fopen("input.txt","r");if (file ==NULL) {printf("Could not open file.\n");exit(EXIT_FAILURE);}
Reading the File
Once we have the file opened, we need to initialize the data. This simply involves reading the array into the map we created. This time, I chose to use fscanf() to get the string, but getline() works too.
// Read data for fileint index =0;map[index] =malloc(100);while (fscanf(file,"%s", map[index]) != EOF) { map[++index] =malloc(100);}
Once this is done, we must initialize the height and width of the map.
// Declare height and width of the heightmapheight = index;width =strlen(map[0]);
Once this is initialized, the processing can begin. We do almost all this in sub-functions, so we'll discuss the looping mechanism in this part and the processing in the next.
We must parse through all the data for both parts. In each part, we check for something different:
Part 1: We check for the starting position S.
Part 2: We check for any a position.
For the BFS algorithm, we simply check to ensure that any finish result is less than the current best case. If it is, we update the best case.
// Find Starting coordinates and calculate shortest pathint part1 = INT_MAX;int part2 = INT_MAX;for (int y =0; y < height; y++) {for (int x =0; x < width; x++) {// part 1 - provided start pointif (map[y][x] =='S') {if (BFS(x, y)< part1) part1 =BFS(x, y); }// part 2 - any 'a' start pointif (map[y][x] =='a') {if (BFS(x, y)< part2) part2 =BFS(x, y); } }}
From here, we can parse the data and perform the BFS algorithm.
Parsing the Data
Here comes the challenge. We need to define a function BFS() that takes a coordinate and returns the shortest path to the end.
The first thing we must do is free queue and visited because we are starting a new BFS algorithm. This ensures the lists are empty.
// Free memory of the queue and visitedfree_memory(queue);free_memory(visited);queue = visited =NULL;
Next, we need to make a new node for the starting position. We'll build nodes often enough that it's worthy of its own function. This function should take the three values we need to make a node, allocate a new one, and return a pointer to the node.
We'll use this to make a new node for the starting position. At the same time, we'll start our queue with this node.
// Create new nodenode *start =buildNode(x, y,0);// Add starting node to the queuequeue = start;
We are going to run the BFS algorithm until the queue is empty. When the queue is empty, we have no more nodes to check. Since BFS doesn't require backtracking, we can make this a simple while loop.
while (queue !=NULL){ /* BFS algorithm */}
We need to get the data from the current node. We'll store this in variables for ease of use.
// Get data from current nodeint x = queue->x;int y = queue->y;int value =getValue(x, y);int steps = queue->steps;
We introduce a new function getValue that finds the value of that position in the map.
intgetValue(int x,int y){char value = map[y][x];switch (value) {case'S':return0;case'E':return25;default:return value -'a'; }}
Why do we define 'S' as 0 and 'E' as 25?
In this case, these represent the start and end positions. We want to ensure that the start position is always the lowest value and the end position is always the highest value. This ensures that we don't backtrack to a position we've already been to.
We need to remove the current node from the queue. We should also add the current node to the visited list.
// Remove node from the queuenode *current = queue;queue = queue->next;current->next =NULL;// Add current node to the visited queuecurrent->next = visited;visited = current;
We need to iterate through the neighbors of the current node. We can define dx and dy to represent the directional arrays for the neighbors.
At each iteration, we need to do the following checks:
Get the coordinates of the neighbor
Check if the neighbor is in the map. If so:
Check if the node is the goal node (E). If so, return the number of steps.
Check if the node is movable. If so:
Build a new node for the neighbor.
Check if the node is already in the queue. If not, add it to the queue.
If it is, free the node.
// Find neighborsfor (int i =0; i <4; i++) {// Get neighbor coordinatesint nx = x + dx[i];int ny = y + dy[i];// Check if neighbor is in the mapif (nx >=0&& nx < width && ny >=0&& ny < height) {// Check if node is the goalif (value >=24&& map[ny][nx] =='E')return steps +1;// check if neighbor is movableint neighbor_value =getValue(nx, ny);if (neighbor_value <= value +1) { node *n =buildNode(nx, ny, steps +1);// Check if node is already in the queueif (!inQueue(n))// Add node to the queueenqueue(&queue, n);elsefree(n); } }}
We made two new functions here.
The first function is inQueue() that checks if the node is in the queue.
intinQueue(node *curr){// Check if node is in the queue node *ptr = queue;while (ptr !=NULL) {if (curr->x ==ptr->x &&curr->y ==ptr->y &&curr->steps >=ptr->steps)return1; ptr =ptr->next; }// Check if node is in the visited queue ptr = visited;while (ptr !=NULL) {if (curr->x ==ptr->x &&curr->y ==ptr->y &&curr->steps >=ptr->steps)return1; ptr =ptr->next; }return0;}
The second function is enqueue() that adds a node to the back of the queue.
voidenqueue(node **list, node *n){if (*list ==NULL) { // If list is empty*list = n; } else { // else add node to the end of the list node *current =*list;while (current->next !=NULL) current =current->next;current->next = n; }}
If we reach the end of the queue and do not return a value, there is no path to the end. In this case, we should return INT_MAX to indicate that there is no path.
This is everything we need to execute a BFS algorithm. We can use this to print the flag.
Printing the Flag
To print the flag, we simply combine the answers from the two parts.
printf("flag{%d:%d}\n", part1, part2);
Once we're done, we free the memory. map is a 2D array, so we need to free each row individually. We also need to free our linked lists and close the file.
// Free memoryfor (int i =0; i < index +1; i++)free(map[i]);free_memory(visited);free_memory(queue);fclose(file);
We define free_memory() to free a Linked List.
voidfree_memory(node *list){// Free memory of the listwhile (list) { node *temp = list; list =list->next;free(temp); }}
Running this method will return the flag! This method will take some time to run, depending on the machine. This method has to run a BFS algorithm for every a in the map. BFS is an O(V + E) algorithm, so the overall time is O(V + E * a). This is a very large number, so running will take some time.
Full Solution
The full solution is below:
solve.c
#include<limits.h>#include<stdio.h>#include<stdlib.h>#include<string.h>typedefstruct node {int x, y, steps;struct node *next;} node;// Global variable declarationchar*map[50];node *queue,*visited;int height, width;// Functions declarationsintBFS(int x,int y);intinQueue(node *n);voidfree_memory(node *list);intgetValue(int x,int y);node *buildNode(int x,int y,int steps);voidenqueue(node **list, node *n);intBFS(int x,int y){// Free memory of the queue and visitedfree_memory(queue);free_memory(visited); queue = visited =NULL;// Create new node node *start =buildNode(x, y,0);// Neighbors coordinatesint dx[]= {1,0,-1,0};int dy[]= {0,1,0,-1};// Add starting node to the queue queue = start;while (queue !=NULL) {// Get data from current nodeint x =queue->x;int y =queue->y;int value =getValue(x, y);int steps =queue->steps;// Remove node from the queue node *current = queue; queue =queue->next;current->next =NULL;// Add current node to the visited queuecurrent->next = visited; visited = current;// Find neighborsfor (int i =0; i <4; i++) {// Get neighbor coordinatesint nx = x + dx[i];int ny = y + dy[i];// Check if neighbor is in the mapif (nx >=0&& nx < width && ny >=0&& ny < height) {// Check if node is the goalif (value >=24&& map[ny][nx] =='E')return steps +1;// check if neighbor is movableint neighbor_value =getValue(nx, ny);if (neighbor_value <= value +1) { node *n =buildNode(nx, ny, steps +1);// Check if node is already in the queueif (!inQueue(n))// Add node to the queueenqueue(&queue, n);elsefree(n); } } } }return INT_MAX;}intinQueue(node *curr){// Check if node is in the queue node *ptr = queue;while (ptr !=NULL) {if (curr->x ==ptr->x &&curr->y ==ptr->y &&curr->steps >=ptr->steps)return1; ptr =ptr->next; }// Check if node is in the visited queue ptr = visited;while (ptr !=NULL) {if (curr->x ==ptr->x &&curr->y ==ptr->y &&curr->steps >=ptr->steps)return1; ptr =ptr->next; }return0;}voidfree_memory(node *list){// Free memory of the listwhile (list) { node *temp = list; list =list->next;free(temp); }}intgetValue(int x,int y){char value = map[y][x];switch (value) {case'S':return0;case'E':return25;default:return value -'a'; }}node *buildNode(int x,int y,int steps){ node *new_node =malloc(sizeof(node));new_node->x = x;new_node->y = y;new_node->steps = steps;new_node->next =NULL;return new_node;}voidenqueue(node **list, node *n){if (*list ==NULL) { // If list is empty*list = n; } else { // else add node to the end of the list node *current =*list;while (current->next !=NULL) current =current->next;current->next = n; }}intmain(void){// Open file for reading FILE *file =fopen("input.txt","r");if (file ==NULL) {printf("Could not open file.\n");exit(EXIT_FAILURE); }// Read data for fileint index =0; map[index] =malloc(100);while (fscanf(file,"%s", map[index])!= EOF) { map[++index] =malloc(100); }// Declare height and width of the heightmap height = index; width =strlen(map[0]);// Find Starting coordinates and calculate shortest pathint part1 = INT_MAX;int part2 = INT_MAX;for (int y =0; y < height; y++) {for (int x =0; x < width; x++) {// part 1 - provided start pointif (map[y][x] =='S') {if (BFS(x, y)< part1) part1 =BFS(x, y); }// part 2 - any 'a' start pointif (map[y][x] =='a') {if (BFS(x, y)< part2) part2 =BFS(x, y); } } }// Print results to consoleprintf("flag{%d:%d}\n", part1, part2);// Free memoryfor (int i =0; i < index +1; i++)free(map[i]);free_memory(visited);free_memory(queue);fclose(file);}