Guest User

AOC16.c

a guest
Jan 8th, 2023
334
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 10.91 KB | None | 0 0
  1. // NOTE
  2. // You will need to amend the input to change any singular `valve XX` line to
  3. // be `valves XX`
  4. #include <assert.h>
  5. #include <ctype.h>
  6. #include <limits.h>
  7. #include <math.h>
  8. #include <stdbool.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <sys/types.h>
  13. #include <time.h>
  14. // #define TEST_MODE
  15. #include "../../lib/queue.h"
  16. #include "../../utils.h"
  17.  
  18. #define MAX_TIME 30
  19.  
  20. typedef struct valve_t {
  21.   // string id "AA"
  22.   char *id;
  23.   // bit field, only set on positive valves
  24.   u_int64_t idBit;
  25.   // obvious
  26.   int flowRate;
  27.   // the raw input string for later parsing
  28.   char *rawValves;
  29.   // pointers to other valves, dynamically allocated array
  30.   struct valve_t **links;
  31.   // number of pointers above
  32.   int linkCount;
  33.   // the cost (in minutes) to move from this valve to any of the other valves
  34.   u_int16_t *valveWeights;
  35.   // the index of the valve in the allValves array
  36.   int index;
  37. } valve_t;
  38.  
  39. valve_t **allValves = NULL;
  40. int valveCount = 0;
  41. valve_t *aaValve = NULL;
  42. u_int64_t idBit = 1;
  43. u_int64_t positiveValveIdBits = 0;
  44. u_int64_t positiveValveCount = 0;
  45.  
  46. typedef struct graph_t {
  47.   // bit field of visited (positive only) valves
  48.   u_int64_t ids;
  49.   // the index in allValves of the most recently opened valve
  50.   int currentIndex;
  51.   // number of visited valves (P2 only)
  52.   int visited;
  53.   // the current minute
  54.   int minute;
  55.   // total released pressure
  56.   int pressure;
  57. } graph_t;
  58.  
  59. void fileHandler(int lines) { allValves = malloc(sizeof(valve_t *) * lines); }
  60.  
  61. void lineHandler(char *line) {
  62.   fprintf(stdout, "line: %s\n", line);
  63.  
  64.   char id[3];
  65.   int flowRate = 0;
  66.   char *valvesStr = strrchr(line, 's') + 2;
  67.   sscanf(line, "Valve %s has flow rate=%d", id, &flowRate);
  68.  
  69.   valve_t *valve = malloc(sizeof(valve_t));
  70.   valve->id = calloc(3, sizeof(char));
  71.   strcpy(valve->id, id);
  72.   valve->idBit = 0;
  73.   valve->index = valveCount;
  74.   valve->flowRate = flowRate;
  75.   valve->linkCount = 0;
  76.   valve->rawValves = calloc(20, sizeof(char));
  77.   strcpy(valve->rawValves, valvesStr);
  78.  
  79.   if (valve->flowRate > 0) {
  80.     valve->idBit = idBit;
  81.     positiveValveIdBits |= idBit;
  82.     positiveValveCount++;
  83.     idBit <<= 1;
  84.   }
  85.  
  86.   // fprintf(stdout, "valve %s -> rate %d -> valves '%s'\n", valve->id,
  87.           // valve->flowRate, valve->rawValves);
  88.   allValves[valveCount++] = valve;
  89.  
  90.   fputs("\n", stdout);
  91. }
  92.  
  93. void connectValves() {
  94.   for (int i = 0; i < valveCount; i++) {
  95.     valve_t *valve = allValves[i];
  96.     // fprintf(stdout, "valve %s (%ld) -> rate %d -> valves '%s'\n", valve->id,
  97.             // valve->idBit, valve->flowRate, valve->rawValves);
  98.  
  99.     int linkCount = 1;
  100.  
  101.     for (char *c = valve->rawValves; *c; c++) {
  102.       if (*c == ',') {
  103.         linkCount++;
  104.       }
  105.     }
  106.  
  107.     valve->links = malloc(sizeof(valve_t *) * linkCount);
  108.     char *valveEnd;
  109.     char *valveToken = strtok_r(valve->rawValves, ", ", &valveEnd);
  110.     do {
  111.       for (int j = 0; j < valveCount; j++) {
  112.         if (strcmp(allValves[j]->id, valveToken) == 0) {
  113.           valve_t *connectedValve = allValves[j];
  114.           valve->links[valve->linkCount++] = connectedValve;
  115.           break;
  116.         }
  117.       }
  118.     } while ((valveToken = strtok_r(NULL, ", ", &valveEnd)) != NULL);
  119.   }
  120.  
  121.   for (int i = 0; i < valveCount; i++) {
  122.     if (strcmp(allValves[i]->id, "AA") == 0) {
  123.       aaValve = allValves[i];
  124.     }
  125.   }
  126. }
  127.  
  128. // Floyd-Warshall
  129. void calculateDistances() {
  130.   // let dist be a |V| × |V| array of minimum distances initialized to ∞
  131.   u_int16_t dist[valveCount][valveCount];
  132.   memset(dist, INT_MAX, sizeof(dist));
  133.   // for each edge (u, v) do
  134.   // dist[u][v] ← w(u, v)  // The weight of the edge (u, v)
  135.   // We just explore the entire valve array and set each valid link to 1
  136.   // I believe this is an adjacency matrix of our graph
  137.   for (int u = 0; u < valveCount; u++) {
  138.     valve_t *valve = allValves[u];
  139.     for (int v = 0; v < valve->linkCount; v++) {
  140.       dist[u][valve->links[v]->index] = 1;
  141.     }
  142.   }
  143.   // for each vertex v do
  144.   // dist[v][v] ← 0
  145.   for (int v = 0; v < valveCount; v++) {
  146.     dist[v][v] = 0;
  147.   }
  148.  
  149.   // for k from 1 to |V|
  150.   for (int k = 0; k < valveCount; k++) {
  151.     // for i from 1 to |V|
  152.     for (int i = 0; i < valveCount; i++) {
  153.       // for j from 1 to |V|
  154.       for (int j = 0; j < valveCount; j++) {
  155.         if (dist[i][j] > dist[i][k] + dist[k][j]) {
  156.           dist[i][j] = dist[i][k] + dist[k][j];
  157.         }
  158.       }
  159.     }
  160.   }
  161.  
  162.   /* debug print for visibility
  163.   for (int i = 0; i < valveCount; i++) {
  164.     for (int j = 0; j < valveCount; j++) {
  165.       fprintf(stdout, "%d ", dist[i][j]);
  166.     }
  167.     fprintf(stdout, "\n");
  168.   }
  169.   */
  170.  
  171.   for (int a = 0; a < valveCount; a++) {
  172.     valve_t *aValve = allValves[a];
  173.     // setup valveWeights array
  174.     // which is an array of int to each valve that has a flowRate
  175.     // only for AA or flowRate > 0
  176.     aValve->valveWeights = calloc(valveCount, sizeof(int));
  177.     if (aValve->flowRate != 0 || strcmp(aValve->id, "AA") == 0) {
  178.       for (int b = 0; b < valveCount; b++) {
  179.         valve_t *bValve = allValves[b];
  180.         if (bValve->flowRate != 0) {
  181.           // only set connections to valves with a positive flowRate
  182.           aValve->valveWeights[b] = dist[a][b];
  183.         }
  184.       }
  185.     }
  186.   }
  187.  
  188.   /* debug print for visibility
  189.   for (int i = 0; i < valveCount; i++) {
  190.     valve_t *valve = allValves[i];
  191.  
  192.     fprintf(stdout, "valve %s, weights: ", valve->id);
  193.     for (int j = 0; j < valveCount; j++) {
  194.       fprintf(stdout, "%d: %d ", j, valve->valveWeights[j]);
  195.     }
  196.     fprintf(stdout, "\n");
  197.   }
  198.   */
  199. }
  200.  
  201. // DFS (or maybe BFS, i have no idea) search of all possible ways to visit each
  202. // positive flowRate valve in the allowed time
  203. int exploreValves() {
  204.   int maxPressure = -1;
  205.  
  206.   int maxTime = MAX_TIME;
  207.   queue_t *queue = q_create();
  208.   graph_t *graphStart = malloc(sizeof(graph_t));
  209.   graphStart->ids = aaValve->idBit;
  210.   graphStart->currentIndex = aaValve->index;
  211.   graphStart->minute = 1;
  212.   graphStart->pressure = 0;
  213.   q_enqueue(queue, graphStart);
  214.  
  215.   queue_t *endQueue = q_create();
  216.  
  217.   while (!q_empty(queue)) {
  218.     graph_t *graph = q_dequeue(queue);
  219.     // break if all are visited or if we have reached the time limit
  220.     if (graph->ids == positiveValveIdBits || graph->minute == maxTime) {
  221.       q_enqueue(endQueue, graph);
  222.       continue;
  223.     }
  224.  
  225.     bool canMove = false;
  226.     for (int i = 0; i < valveCount; i++) {
  227.       valve_t *valve = allValves[i];
  228.       int valveMoveTime = allValves[graph->currentIndex]->valveWeights[i];
  229.  
  230.       // for every positive valve
  231.       // that we have not yet visited
  232.       // that we can move and open in the time limit
  233.       if (valve->flowRate > 0 && (graph->ids & valve->idBit) == 0 &&
  234.           graph->minute + valveMoveTime + 1 < maxTime) {
  235.         graph_t *newGraph = malloc(sizeof(graph_t));
  236.         newGraph->ids = graph->ids;
  237.         newGraph->ids |= valve->idBit;
  238.         newGraph->currentIndex = valve->index;
  239.         newGraph->minute = graph->minute + valveMoveTime + 1;
  240.         newGraph->pressure =
  241.             graph->pressure +
  242.             (valve->flowRate * (maxTime - graph->minute - valveMoveTime));
  243.         q_enqueue(queue, newGraph);
  244.         canMove = true;
  245.       }
  246.     }
  247.     // reached a configuration that can't progress in time limit
  248.     if (!canMove) {
  249.       q_enqueue(endQueue, graph);
  250.       continue;
  251.     }
  252.     free(graph);
  253.   }
  254.   q_destroy(queue);
  255.  
  256.   while (!q_empty(endQueue)) {
  257.     graph_t *graph = q_dequeue(endQueue);
  258.     if (graph->pressure > maxPressure) {
  259.       maxPressure = graph->pressure;
  260.     }
  261.     free(graph);
  262.   }
  263.   q_destroy(endQueue);
  264.  
  265.   return maxPressure;
  266. }
  267.  
  268. // same as above, but elephant must visit a different valve than the human
  269. int exploreValvesWithElephant() {
  270.   int maxPressure = -1;
  271.  
  272.   int maxTime = MAX_TIME - 4;
  273.   int maxValves = floor(positiveValveCount / 2.0);
  274.   queue_t *queue = q_create();
  275.   graph_t *graphStart = malloc(sizeof(graph_t));
  276.   graphStart->visited = 0;
  277.   graphStart->ids = aaValve->idBit;
  278.   graphStart->currentIndex = aaValve->index;
  279.   graphStart->minute = 1;
  280.   graphStart->pressure = 0;
  281.   q_enqueue(queue, graphStart);
  282.  
  283.   queue_t *endQueue = q_create();
  284.  
  285.   while (!q_empty(queue)) {
  286.     graph_t *graph = q_dequeue(queue);
  287.     // break if all are visited
  288.     // or if we have reached the time limit
  289.     // or if we have reached maxValves
  290.     if (graph->ids == positiveValveIdBits || graph->minute == maxTime ||
  291.         graph->visited == maxValves) {
  292.       q_enqueue(endQueue, graph);
  293.       continue;
  294.     }
  295.  
  296.     bool canMove = false;
  297.     for (int i = 0; i < valveCount; i++) {
  298.       valve_t *valve = allValves[i];
  299.       int valveMoveTime = allValves[graph->currentIndex]->valveWeights[i];
  300.  
  301.       // for every positive valve
  302.       // that we have not yet visited
  303.       // that we can move and open in the time limit
  304.       if (valve->flowRate > 0 && (graph->ids & valve->idBit) == 0 &&
  305.           graph->minute + valveMoveTime + 1 < maxTime) {
  306.         graph_t *newGraph = malloc(sizeof(graph_t));
  307.         newGraph->visited = graph->visited + 1;
  308.         newGraph->ids = graph->ids;
  309.         newGraph->ids |= valve->idBit;
  310.         newGraph->currentIndex = valve->index;
  311.         newGraph->minute = graph->minute + valveMoveTime + 1;
  312.         newGraph->pressure =
  313.             graph->pressure +
  314.             (valve->flowRate * (maxTime - graph->minute - valveMoveTime));
  315.         q_enqueue(queue, newGraph);
  316.         canMove = true;
  317.       }
  318.     }
  319.     // reached a configuration that can't progress in time limit
  320.     if (!canMove) {
  321.       q_enqueue(endQueue, graph);
  322.       continue;
  323.     }
  324.     free(graph);
  325.   }
  326.   q_destroy(queue);
  327.  
  328.   // now our endQueue needs to go into an array
  329.   graph_t **endArray = calloc(endQueue->size, sizeof(graph_t *));
  330.   int endIndex = 0;
  331.   while (!q_empty(endQueue)) {
  332.     graph_t *graph = q_dequeue(endQueue);
  333.     endArray[endIndex++] = graph;
  334.   }
  335.  
  336.   for (int a = 0; a < endIndex; a++) {
  337.     // find the highest disjointed combination of nodes
  338.     graph_t *aGraph = endArray[a];
  339.  
  340.     for (int b = 0; b < endIndex; b++) {
  341.       graph_t *bGraph = endArray[b];
  342.       // if each set of ids is different then we will get zero
  343.       // 0010 & 0100 == 0000
  344.       // 0011 & 0010 == 0010
  345.       if ((aGraph->ids & bGraph->ids) == 0) {
  346.         int total = aGraph->pressure + bGraph->pressure;
  347.         if (total > maxPressure) {
  348.           maxPressure = total;
  349.         }
  350.       }
  351.     }
  352.   }
  353.   q_destroy(endQueue);
  354.  
  355.   return maxPressure;
  356. }
  357.  
  358. int main() {
  359.   readInputFile(__FILE__, lineHandler, fileHandler);
  360.   srand(time(NULL));
  361.   connectValves();
  362.   calculateDistances();
  363.  
  364.   int partOne = exploreValves();
  365.  
  366.   fprintf(stdout, "Part one: %d\n", partOne);
  367.  
  368. #ifdef TEST_MODE
  369.   assert(partOne == 1651);
  370. #else
  371.   assert(partOne == 1896);
  372. #endif
  373.  
  374.   int partTwo = exploreValvesWithElephant();
  375.  
  376.   fprintf(stdout, "Part two: %d\n", partTwo);
  377. #ifdef TEST_MODE
  378.   assert(partTwo == 1707);
  379. #else
  380.   assert(partTwo == 2576);
  381. #endif
  382. }
Advertisement
Add Comment
Please, Sign In to add comment