advent-of-code-2021/8/8-2.c

294 lines
8.8 KiB
C
Raw Normal View History

2021-12-09 01:46:39 +08:00
/*
* HIGHLY OVERCOMPLICATED SOLUTION USING BOOLEAN LOGIC
* THE MAPPING LOGIC WAS SKETCHED OUT ON PAPER :
* WE KNOW THAT 1 HAS TWO SEGMENTS ON THE RIGHT, AND 7 IS A ONE WITH AN EXTRA TOP
SEGMENT. SO WE KNOW THE VALUE OF THIS TOP SEGMENT.
* COMBINING 7 AND 4 RESULTS IN A SHAPE SIMILAR TO 9 (MISSING THE BOTTOM SEGMENT)
SEARCH THROUGH THE LIST FOR SOMETHING WITH 6 SEGMENTS ACTIVE AND ONLY ONE
DIFFERENCE FROM THE UNION OF 7 & 4. THIS IS THE BOTTOM SEGMENT.
* COMBINE THE TOP AND BOTTOM SEGMENTS AND ONE. DO THE SAME PREVIOUS PROCESS BUT
WITH THIS NEW UNION AND THE NUMBER 3. WE NOW KNOW THE MIDDLE SEGMENT
* COMBINE ONE AND TOP, MIDDLE AND BOTTOM SEGMENTS. USE THE 9 WE FOUND EARLIER
TO FIND THE TOP LEFT SEGMENT
* COMBINE TOP, MIDDLE, BOTTOM, LEFT TOP SEGMENTS AND FIND 5 TO GET THE BOTTOM
RIGHT SEGMENT
* USING 1 AND THE BOTTOM RIGHT SEGMENT GET THE TOP RIGHT SEGMENT
* USING 8 AND ALL THE SEGMENTS WE'VE COLLECTED, FIND THE BOTTOM LEFT SEGMENT
*/
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
// STRUCTURE OF INPUT
#define ENTRIES 200
#define DIGIT_LEN 10
#define SEGMENT_GROUP_LEN 4
// RELATIONSHIP DEFINES
#define INPUT_COUNT 10
#define OUTPUT_COUNT 4
#define CABLES 7
#define CHAR_BIN(char) (1 << ((char)-'a'))
typedef struct
{
char top;
char right_top;
char right_bottom;
char left_top;
char left_bottom;
char bottom;
char middle;
} SEVEN_SEGMENT;
typedef struct
{
u_int8_t value;
u_int8_t enabled;
} DIGIT;
DIGIT *new_digit(u_int8_t value)
{
DIGIT *p_digit = calloc(1, sizeof(DIGIT));
p_digit->value = value;
return p_digit;
}
void set_digit(DIGIT *digit, char position, bool state)
{
if (state)
{
digit->value |= CHAR_BIN(position);
digit->enabled++;
}
else
{
digit->value &= ~(CHAR_BIN(position));
digit->enabled--;
}
}
DIGIT str_to_digit(char *digit_str)
{
DIGIT digit = {0};
size_t len = strlen(digit_str);
for (size_t i = 0; i < len; i++)
{
set_digit(&digit, digit_str[i], true);
}
return digit;
}
u_int8_t get_digit_value(DIGIT *mappings[], DIGIT digit)
{
for (size_t i = 0; i < DIGIT_LEN; i++)
{
if (digit.value == mappings[i]->value)
return i;
}
exit(EXIT_FAILURE);
}
char position_value(u_int8_t position_bin)
{
char result = 'a';
while (position_bin >>= 1)
result++;
return result;
}
// TRUE IF NUMBER CONTAINS ALL DIGITS IN CONTAINED
bool contains(u_int8_t number, u_int8_t contained)
{
for (size_t i = 0; i < 8; i++)
{
if ((contained >> i) & 1 && !((number >> i) & 1))
{
return false;
}
}
return true;
}
// ---
// DO MAPPINGS
void set_digit_mapping(char **input, DIGIT **digit_mapping)
{
SEVEN_SEGMENT seven_segment;
DIGIT *unknown_digits = calloc(DIGIT_LEN, sizeof(DIGIT)); // This will result in a memory leak on big datasets, because we cannot free it easily.
// DECODE WIRES TO SEGMENTS
for (size_t i = 0; i < DIGIT_LEN; i++)
{
unknown_digits[i] = str_to_digit(input[i]);
// TRIVIAL, UNIQUE LENGTH CASES
switch (unknown_digits[i].enabled)
{
case 7: // EIGHT
digit_mapping[8] = &unknown_digits[i];
break;
case 3: // SEVEN
digit_mapping[7] = &unknown_digits[i];
break;
case 4: // FOUR
digit_mapping[4] = &unknown_digits[i];
break;
case 2: // ONE
digit_mapping[1] = &unknown_digits[i];
break;
}
}
// DIGITS 7 3 4 2 ARE KNOWN
// TOP PART BY XORING 7 AND 1
seven_segment.top = position_value(digit_mapping[7]->value ^ digit_mapping[1]->value);
// BOTTOM PART BY XORING 9 (UNKNOWN, SEARCH) WITH THE UNION OF 7 AND 4
for (size_t i = 0; i < DIGIT_LEN; i++)
{
u_int8_t union_7_4 = digit_mapping[7]->value | digit_mapping[4]->value;
if (unknown_digits[i].enabled == 6 && contains(unknown_digits[i].value, union_7_4))
{
seven_segment.bottom = position_value(~union_7_4 & unknown_digits[i].value);
digit_mapping[9] = &unknown_digits[i];
break;
}
}
// MIDDLE PART BY XORING 3 (UNKNOWN, SEARCH) WITH THE UNION OF 1 AND TOP/BOTTOM SEGMENTS
for (size_t i = 0; i < DIGIT_LEN; i++)
{
u_int8_t union_b_7 = CHAR_BIN(seven_segment.bottom) | digit_mapping[7]->value;
if (unknown_digits[i].enabled == 5 && contains(unknown_digits[i].value, union_b_7))
{
seven_segment.middle = position_value(~union_b_7 & unknown_digits[i].value);
break;
}
}
// TOP LEFT FROM 9 XOR WITH 3
digit_mapping[3] = new_digit(digit_mapping[7]->value | CHAR_BIN(seven_segment.middle) | CHAR_BIN(seven_segment.bottom)); // SET 3
seven_segment.left_top = position_value(digit_mapping[9]->value ^ digit_mapping[3]->value);
// BOTTOM LEFT FROM 5 XOR WITH SEGMENTS TOP, MIDDLE, BOTTOM, TOP LEFT
for (size_t i = 0; i < DIGIT_LEN; i++)
{
u_int8_t union_t_b_m_lt = CHAR_BIN(seven_segment.top) | CHAR_BIN(seven_segment.bottom) | CHAR_BIN(seven_segment.middle) | CHAR_BIN(seven_segment.left_top);
if (unknown_digits[i].enabled == 5 && contains(unknown_digits[i].value, union_t_b_m_lt))
{
seven_segment.right_bottom = position_value(~union_t_b_m_lt & unknown_digits[i].value);
digit_mapping[5] = &unknown_digits[i];
break;
}
}
// TOP RIGHT FROM ONE XOR BOTTOM RIGHT SEGMENT
seven_segment.right_top = position_value(CHAR_BIN(seven_segment.right_bottom) ^ digit_mapping[1]->value);
// BOTTOM LEFT FROM 8 XOR EVERY OTHER SEGMENT
seven_segment.left_bottom = position_value(digit_mapping[8]->value ^ (digit_mapping[1]->value | digit_mapping[5]->value));
// FINISH MAPPING WIRES TO SEGMENTS
// CONSTRUCT REMAINING DIGITS
// DIGITS nine, eight, seven, five, four, three, one ARE KNOWN
digit_mapping[0] = new_digit(digit_mapping[8]->value ^ CHAR_BIN(seven_segment.middle)); // ZERO
digit_mapping[2] = new_digit(digit_mapping[8]->value ^ (CHAR_BIN(seven_segment.left_top) | CHAR_BIN(seven_segment.right_bottom))); // TWO
digit_mapping[6] = new_digit(digit_mapping[5]->value | CHAR_BIN(seven_segment.left_bottom)); // SIX
}
unsigned int entry_to_number(DIGIT **digit_mapping, char **numbers)
{
unsigned int number = 0;
unsigned int shift = 1;
for (int i = (SEGMENT_GROUP_LEN - 1); i >= 0; i--)
{
number += shift * get_digit_value(digit_mapping, str_to_digit(numbers[i]));
shift *= 10;
}
return number;
}
// ---
// RELATIONSHIP datatype
typedef struct
{
char **input;
char **output;
} RELATIONSHIP;
RELATIONSHIP *new_relationship()
{
RELATIONSHIP *p_relationship = calloc(1, sizeof(RELATIONSHIP));
p_relationship->input = calloc(INPUT_COUNT, sizeof(char *));
p_relationship->output = calloc(OUTPUT_COUNT, sizeof(char *));
for (size_t i = 0; i < INPUT_COUNT; i++)
{
p_relationship->input[i] = calloc(CABLES, sizeof(char));
}
for (size_t i = 0; i < OUTPUT_COUNT; i++)
{
p_relationship->output[i] = calloc(CABLES, sizeof(char));
}
return p_relationship;
}
RELATIONSHIP *string_2_relationship(char *string)
{
RELATIONSHIP *p_relationship = new_relationship();
char input_buf[BUFSIZ];
char output_buf[BUFSIZ];
sscanf(string, "%[^|] | %[^\n]\n", input_buf, output_buf);
char *wire = strtok(input_buf, " ");
for (size_t i = 0; wire != NULL; i++)
{
p_relationship->input[i] = strdup(wire);
wire = strtok(NULL, " ");
}
wire = strtok(output_buf, " ");
for (size_t i = 0; wire != NULL; i++)
{
p_relationship->output[i] = strdup(wire);
wire = strtok(NULL, " ");
}
return p_relationship;
}
//
RELATIONSHIP **read_input(FILE *p_file)
{
char buffer[BUFSIZ];
RELATIONSHIP **p_wire_relationships = calloc(ENTRIES, sizeof(RELATIONSHIP *));
for (size_t i = 0; fgets(buffer, sizeof buffer, p_file) > 0; i++)
{
p_wire_relationships[i] = string_2_relationship(buffer);
}
return p_wire_relationships;
}
int main(int argc, char const *argv[])
{
FILE *p_file = fopen("input", "r");
RELATIONSHIP **p_wire_relationships = read_input(p_file); // I realize now that we could do this line-by-line actualy and save memory.
unsigned int total = 0;
for (size_t i = 0; i < ENTRIES; i++)
{
DIGIT **digit_mapping = calloc(DIGIT_LEN, sizeof(DIGIT *));
set_digit_mapping(p_wire_relationships[i]->input, digit_mapping);
unsigned int number = entry_to_number(digit_mapping, p_wire_relationships[i]->output);
printf("%d\n", number);
total += number;
free(digit_mapping);
}
printf("%d\n", total);
return 0;
}