Question 1) Here we will check the proposed journey path is possible given the provided pony capacity, pony assignment to route segments, and village elevation. The journey path is provided, it should always start at the start village (0,0) and finish at the final village (M,N). The final point (M,N) is at the corner - that is, M and N are the maximum indicies that x and y can take. Write a function check_path(elevations, path, capacities, assignments) Arguments: elevations – a list of rows, where each row is a list of cells, each cell (village) is provided with its absolute elevation level path – a list of tuples, each tuple provides (x,y) coordinates for each path segment to take the trolley on. Should start with (0,0) and finish at (M,N) capacities– a list of available pony types assignments – a list of assignments (tuples) for each pair of ponies to a path segment. The function returns None if the assigned ponies are capable of getting from the initial (0,0) point to the destination using the provided path. Otherwise, it should return a tuple with a cell coordinate and associated error message string. If there are multiple possible error messages, return the first one in the error message list. The possible error messages are provided below. Note that the (M,N) message requires replacing (M,N) with the appropriate values. Cell coordinate is out of bounds A cell listed in path is out of map boundaries according to cell_elevations Path should start at (0,0) coordinate The first element in the path isn't (0,0) Path should end at (M, N) coordinate The path's last element isn't the final village (replace M, N in the error message with values for the current data, where M and N are the maximum indicies that x and y can take). Illegal move The path contains any move other than plus one x coordinate or plus one y coordinate.(*) Insufficient capacity assignment Assigned pony capacity isn't enough to overcome an elevation raise on the way to the next cell in path. (*) Exceeding pony limit A pony assignment for a move contains more than two ponies(*) (*) Whenever an error is associated to a move (source cell -> destination cell), return the move's source cell as the error_cell. If multiple errors occur, report one listed first in the description table; of errors of the same kind, return one occurring earlier in a path >>> # We’ve got ponies of three types >>> pony_capacities = [17, 37, 73] >>> # The path contains 7 cells, requires 6 moves. The map size is 2x6. >>> journey_path = [(0,0), (0,1), (0,2), (0,3), (1,3), (1,4), (1,5)] >>> # Ponies assigned for each move (matched by corresponding indices). Every time we need a pair of them. >>> pony_assignments = [(73, 73)] * 6 >>> # Absolute elevation levels for each cell (village) on the map (2x6 = 12) >>> village_elevations = [[100, 200, 300, 400, 100, 600], [0, 100, 200, 300, 400, 500]] >>> result = check_path(village_elevations, journey_path, pony_capacities, pony_assignments) >>> print(result) None >>> journey_path = [(0,0), (0,1), (0,2), (0,3), (1,3), (1,4), (1,6)] >>> check_path(village_elevations, journey_path, pony_capacities, pony_assignments) ((1, 6), 'Cell coordinate is out of bounds') >>> journey_path = [(0,1), (0,1), (0,2), (0,3), (1,3), (1,4), (1,5)] >>> check_path(village_elevations, journey_path, pony_capacities, pony_assignments) ((0, 1), 'Path should start at (0,0) coordinate’) Function codes: Output: Question 2) In the previous question we provided ponies for each segment. That is often not the case in real-world scenarios. In this question, we will write a function to assign ponies for each segment on the path provided. The function should find the best allocation according to the following criteria: * (the main optimization criterion) 1. Assignment is superior if it has lower total unused pony capacity * (an extra heuristic to resolve a tie at criterion 1) 2. Smaller differences between the capacity of two assigned ponies are preferred * [Used in Q3 only] (an extra heuristic to resolve a tie at criterion 2) 3. If moves are different, prefer the one with greater elevation raise * [Used in Q3 only] (an extra heuristic to resolve a tie at criterion 3) 4. Prefer moves into lower value (x,y) coordinates Here "lower" means (x1,y1)<(x2,y2). The last criterion is used to make our solutions more deterministic. Write a function assign_pony_to_path(elevations, path, capacities) Arguments: elevations – a list of rows, where each row is a list of cells, each cell (village) is provided with its absolute elevation level path – a list of tuples, each tuple provides (x,y) coordinates for each path segment to take the trolley on. Should start with (0,0) and finish at (M,N) capacities – a list of elevations by which every pony can pull the trolley The function should return a list assignments – a list of assignments (tuples) for each pair of ponies to a path segment. If there are two possible ways to create a pony pair, then use the one with the smaller value first. For example, if (37, 73) and (73, 37) are both valid pairs, then keep (37, 73). You may assume all inputs are well-formatted. >>> pony_capacities = [17, 37, 73] >>> journey_path = [(0,0), (0,1), (0,2), (0,3), (1,3), (1,4), (1,5)] >>> pony_assignments = [(73, 73)] * 6 >>> village_elevations = [[ 100, 200, 300, 400, 100, 600], [0, 100, 200, 300, 400, 500 ]] >>> lower_village_elevations = [[ 10, 20, 30, 40, 10, 60], [0, 10, 20, 30, 40, 50 ]] >>> assign_pony_to_path(village_elevations, journey_path, pony_capacities) [(37, 73), (37, 73), (37, 73), (17, 17), (37, 73), (37, 73)] >>> assign_pony_to_path(lower_village_elevations, journey_path, pony_capacities) [(17, 17), (17, 17), (17, 17), (17, 17), (17, 17), (17, 17)] >>> another_journey_path = [(0,0), (1,0), (1,1), (1,2), (1,3), (1,4), (1,5)] >>> assign_pony_to_path(village_elevations, another_journey_path, pony_capacities) [(17, 17), (37, 73), (37, 73), (37, 73), (37, 73), (37, 73)] >>> impossible_journey_path = [(0,0), (0,1), (0,2), (0,3), (0,4), (0,5), (1,5)] >>> assign_pony_to_path(village_elevations, impossible_journey_path, pony_capacities) [(37, 73), (37, 73), (37, 73), (17, 17), None, (17, 17)] Hint * It would be a nice idea to use helper functions. For example, possible helper function(s) can (1) compare two assignments and decide which one is better, and/or (2) find the best pony allocation for a given move, etc. Split your logic into defined units, with individual functions for each section of logic! * Using helper functions (instead of having a very long and complicated main function) improves readability and makes your life easier. * You may want to use these helper functions in Q2, Q3 and in Project 2. * Allocation criteria are hierarchical, so we can represent an allocation's score as a tuple where component scores are enumerated in (descending) order of priority. * If you choose to create allocation scores in this way, you’ll be able to use Python’s built-in comparison operators like <, ≤, >, ≥ to determine which score is superior. A tie-resolution decision procedure will be properly implemented by the operators. * Consider two tuples (a1, b1, c1) and (a2, b2, c2). An operator (a1, b1, c1) > (a2, b2, c2) will return True if a1 > a2 regardless of b1, c1, b2, c2 values. Otherwise, if a1==a2, it will consider b1 and b2. If b1 > b2, it will return True; otherwise, if b1==b2, it will proceed to compare c1 and c2. If c1 > c2, it will return True; otherwise, its result will be False. Function for Q2: Question 3). In this question we are neither provided with a path nor an assignment of ponies. Our task is to get the trolley from the initial point (0,0) to the final point (M,N). Recall that the final point (M,N) is at the corner, that is, M and N are the maximum values that x and y can take. Try choosing the path step direction and pony assignment at each time step, one at a time, starting from the initial point (0,0). Specifically, you will need to use a greedy algorithm. Greedy algorithms make the best available choice at each step, regardless of whether that choice leads to the overall optimal solution. You can see more info here. As an example, you want to buy a 12-dollar meal using the smallest number of coins, and the available coins are 5-dollar, 4-dollar and 1-dollar coins. What would you do? The optimal solution would be to use three 4-dollar coins. But if you’re using a greedy algorithm, you will select coins one at a time and always select the coin with the largest value – you will select a 5-dollar coin, another 5-dollar coin, a 1-dollar coin, and then another 1-dollar coin, resulting in using 4 coins total instead of just 3. The greedy algorithm that we will use for this question works in a similar way to the coin problem: At each location (x, y), we consider either moving to the next location (x+1, y) or (x, y+1). We choose the move by comparing the four criteria (described below) for the two possible next locations. Then we repeat the same procedure until the final destination cell is reached. For example, at (0,0), we first choose to go to (0,1) because it is better than (1,0) based on the four criteria. Then from (0,1), we choose to go to (0,2); then we go to (1,2) from (0,2)… until we reach (M,N). Similar to the previous question, the following criteria should be used (make sure you follow the order): 1. Assignment is superior if it has lower total unused pony capacity 2. Prefer smaller differences between the capacity of two assigned ponies 3. If moves are different, prefer the one with greater elevation raise 4. Prefer moves into lower(x,y) coordinates Apply the first rule and, if some move assignments are preferrable based on the first rule, adopt this decision. If some move assignments tie in the first rule, we proceed to the next rule, and so on. Check the details in Q2. Write the function find_path_greedy(elevations, capacities) Arguments: elevations - a list of rows, where each row is a list of cells, each cell (village) is provided with its absolute elevation level capacities – a list of elevations by which every pony can pull the trolley The function should return a tuple with path and assignments. The structure of variables should be consistent with that in previous questions. It is possible that no allocation exists for some point on the path. In the example image:  Figure 2 No allocation exists for the next move at the location (5,5). In this case, we return the partial assignments and partial path (i.e. the path is [(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (2, 3), (2, 4), (3, 4), (3, 5), (4, 5), (5, 5)] in this example) until the location where we get stuck. >>> pony_capacities = [17, 37, 73] >>> journey_path = [(0,0), (0,1), (0,2), (0,3), (1,3), (1,4), (1,5)] >>> village_elevations = [[ 100, 200, 300, 400, 100, 600], [0, 100, 200, 300, 400, 500 ]] >>> path, assignment = find_path_greedy(village_elevations, pony_capacities) >>> path [(0, 0), (0, 1), (0, 2), (0, 3), (1, 3), (1, 4), (1, 5)] >>> assignment [(37, 73), (37, 73), (37, 73), (17, 17), (37, 73), (37, 73) Function for Q3: Output for Q2 Q3