364 lines
11 KiB
C++
364 lines
11 KiB
C++
#include "wires_puzzle.h"
|
|
|
|
const static char* WIRES_FILE_PATH = "/sdcard/wires.txt";
|
|
const static char* TAG = "wires_puzzle";
|
|
|
|
static int color_name_len[NUM_COLORS] = {
|
|
3, // red
|
|
6, // yellow
|
|
5, // green
|
|
4, // blue
|
|
5, // black
|
|
5, // white
|
|
};
|
|
|
|
static int max(int n0, int n1, int n2, int n3, int n4, int n5);
|
|
|
|
void wires_to_string(WireColor* wires, char* out_wires_string) {
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
switch (wires[i]) {
|
|
case WireColor::red:
|
|
out_wires_string[i] = 'r';
|
|
break;
|
|
case WireColor::yellow:
|
|
out_wires_string[i] = 'y';
|
|
break;
|
|
case WireColor::green:
|
|
out_wires_string[i] = 'g';
|
|
break;
|
|
case WireColor::blue:
|
|
out_wires_string[i] = 'b';
|
|
break;
|
|
case WireColor::black:
|
|
out_wires_string[i] = 'k';
|
|
break;
|
|
case WireColor::white:
|
|
out_wires_string[i] = 'w';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
void cut_to_string(bool* cut, char* out_cut_string) {
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
if (cut[i]) {
|
|
out_cut_string[i] = 'c';
|
|
} else {
|
|
out_cut_string[i] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
void string_to_wires(char* wires_string, WireColor* out_wires) {
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
switch (wires_string[i]) {
|
|
case 'r':
|
|
out_wires[i] = WireColor::red;
|
|
break;
|
|
case 'y':
|
|
out_wires[i] = WireColor::yellow;
|
|
break;
|
|
case 'g':
|
|
out_wires[i] = WireColor::green;
|
|
break;
|
|
case 'b':
|
|
out_wires[i] = WireColor::blue;
|
|
break;
|
|
case 'k':
|
|
out_wires[i] = WireColor::black;
|
|
break;
|
|
case 'w':
|
|
out_wires[i] = WireColor::white;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void save_wires_to_sd_card(WireColor* wires) {
|
|
FILE* f = fopen(WIRES_FILE_PATH, "w");
|
|
if (f == NULL) {
|
|
ESP_LOGE(TAG, "Failed to open wires file to write");
|
|
}
|
|
char wires_string[NUM_WIRES+1] = {0};
|
|
wires_to_string(wires, wires_string);
|
|
fprintf(f, wires_string);
|
|
fclose(f);
|
|
}
|
|
|
|
void load_wires_from_sd_card(WireColor* out_wires) {
|
|
FILE* f = fopen(WIRES_FILE_PATH, "r");
|
|
if (f == NULL) {
|
|
ESP_LOGW(TAG, "Failed to read wires file. Generating new wires");
|
|
generate_new_wires(out_wires);
|
|
save_wires_to_sd_card(out_wires);
|
|
return;
|
|
}
|
|
char wires_string[NUM_WIRES+1] = {0};
|
|
fgets(wires_string, sizeof(wires_string), f);
|
|
fclose(f);
|
|
|
|
string_to_wires(wires_string, out_wires);
|
|
}
|
|
|
|
/// @brief Fills the array with 6 random WiresColors.
|
|
/// @param wires and array of len >= 6 to be populated with random colors
|
|
void generate_new_wires(WireColor* wires) {
|
|
for (int w = 0; w < NUM_WIRES; w++) {
|
|
// roughly evenly distributed
|
|
uint32_t rand = esp_random() % NUM_COLORS;
|
|
wires[w] = (WireColor)(rand);
|
|
}
|
|
|
|
bool cuts[NUM_WIRES] = {0};
|
|
solve_wires(wires, cuts);
|
|
int num_cuts = 0;
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
if (cuts[i]) num_cuts++;
|
|
}
|
|
|
|
// regenerate if there are less than 3 cuts.
|
|
if (num_cuts < 3) {
|
|
return generate_new_wires(wires);
|
|
}
|
|
}
|
|
|
|
void solve_wires(WireColor* wires, bool* out_cut) {
|
|
// by default, don't cut any wires
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
out_cut[i] = false;
|
|
}
|
|
|
|
// Find all positions of all wire colors
|
|
int red_pos[NUM_WIRES + 1] = {0};
|
|
int red_pos_len = 0;
|
|
int yellow_pos[NUM_WIRES + 1] = {0};
|
|
int yellow_pos_len = 0;
|
|
int green_pos[NUM_WIRES + 1] = {0};
|
|
int green_pos_len = 0;
|
|
int blue_pos[NUM_WIRES + 1] = {0};
|
|
int blue_pos_len = 0;
|
|
int black_pos[NUM_WIRES + 1] = {0};
|
|
int black_pos_len = 0;
|
|
int white_pos[NUM_WIRES + 1] = {0};
|
|
int white_pos_len = 0;
|
|
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
if (wires[i] == WireColor::red) {
|
|
red_pos[red_pos_len++] = i;
|
|
} else if (wires[i] == WireColor::yellow) {
|
|
yellow_pos[yellow_pos_len++] = i;
|
|
} else if (wires[i] == WireColor::green) {
|
|
green_pos[green_pos_len++] = i;
|
|
} else if (wires[i] == WireColor::blue) {
|
|
blue_pos[blue_pos_len++] = i;
|
|
} else if (wires[i] == WireColor::black) {
|
|
black_pos[black_pos_len++] = i;
|
|
} else if (wires[i] == WireColor::white) {
|
|
white_pos[white_pos_len++] = i;
|
|
}
|
|
}
|
|
|
|
int* list_pos[NUM_COLORS] = {
|
|
red_pos,
|
|
yellow_pos,
|
|
green_pos,
|
|
blue_pos,
|
|
black_pos,
|
|
white_pos
|
|
};
|
|
int list_pos_len[NUM_COLORS] = {
|
|
red_pos_len,
|
|
yellow_pos_len,
|
|
green_pos_len,
|
|
blue_pos_len,
|
|
black_pos_len,
|
|
white_pos_len
|
|
};
|
|
|
|
// CUT CHECKS
|
|
|
|
// 1. cut the second wire of all most common colors
|
|
int max_len = max(red_pos_len, yellow_pos_len, green_pos_len, blue_pos_len, black_pos_len, white_pos_len);
|
|
if (max_len >= 2) {
|
|
for (int i = 0; i < NUM_COLORS; i++) {
|
|
if (list_pos_len[i] == max_len) {
|
|
int idx = list_pos[i][1];
|
|
out_cut[idx] = true;
|
|
ESP_LOGI(TAG, "1. cutting %d", idx);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 2. cut the first wire if it is green or white
|
|
if (wires[0] == WireColor::green || wires[0] == WireColor::white) {
|
|
out_cut[0] = true;
|
|
ESP_LOGI(TAG, "2. cutting %d", 0);
|
|
}
|
|
|
|
// 3. cut blue wires in even positions (odd indexes)
|
|
for (int i = 1; i < NUM_WIRES; i += 2) {
|
|
if (wires[i] == WireColor::blue) {
|
|
out_cut[i] = true;
|
|
ESP_LOGI(TAG, "3. cutting %d", i);
|
|
}
|
|
}
|
|
|
|
// 4. cut black and yellow wires next to black and yellow wires
|
|
for (int i = 0; i < NUM_WIRES-1; i++) {
|
|
if (
|
|
(wires[i] == WireColor::yellow || wires[i] == WireColor::black) &&
|
|
(wires[i+1] == WireColor::yellow || wires[i+1] == WireColor::black)
|
|
) {
|
|
out_cut[i] = true;
|
|
out_cut[i+1] = true;
|
|
ESP_LOGI(TAG, "4. cutting %d, %d", i, i+1);
|
|
}
|
|
}
|
|
|
|
// 5. cut the last green wire next to a yellow or white wire
|
|
for (int green_idx = green_pos_len-1; green_idx >= 0; green_idx--) {
|
|
int pos = green_pos[green_idx];
|
|
if (
|
|
wires[pos-1] == WireColor::yellow ||
|
|
wires[pos-1] == WireColor::white ||
|
|
wires[pos+1] == WireColor::yellow ||
|
|
wires[pos+1] == WireColor::white
|
|
) {
|
|
out_cut[pos] = true;
|
|
ESP_LOGI(TAG, "5. cutting %d", pos);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 6. cut all white wires if there is a red wire in the 5th position
|
|
if (wires[4] == WireColor::red) {
|
|
for (int white_idx = 0; white_idx < white_pos_len; white_idx++) {
|
|
out_cut[white_pos[white_idx]] = true;
|
|
ESP_LOGI(TAG, "6. cutting %d", white_pos[white_idx]);
|
|
}
|
|
}
|
|
|
|
// 7. cut the first black wire if there are more white wires than green wires
|
|
if (white_pos_len > green_pos_len && black_pos_len > 0) {
|
|
out_cut[black_pos[0]] = true;
|
|
ESP_LOGI(TAG, "7. cutting %d", black_pos[0]);
|
|
}
|
|
|
|
// 8. cut all wires in an alternating pattern of 2 colors at least 4 wires long
|
|
for (int i = 0; i < NUM_WIRES-3; i++) {
|
|
if (
|
|
wires[i] == wires[i+2] &&
|
|
wires[i+1] == wires[i+3] &&
|
|
wires[i] == wires[i+1]
|
|
) {
|
|
out_cut[i] = true;
|
|
out_cut[i+1] = true;
|
|
out_cut[i+2] = true;
|
|
out_cut[i+3] = true;
|
|
ESP_LOGI(TAG, "8. cutting %d, %d, %d, %d", i, i+1, i+2, i+3);
|
|
}
|
|
}
|
|
|
|
// 9. cut any wires if their position matches the number of letters in the color's name
|
|
for (int i = 0; i < NUM_WIRES; i++) {
|
|
if (color_name_len[wires[i]] == i+1) {
|
|
out_cut[i] = true;
|
|
ESP_LOGI(TAG, "9. cutting %d", i);
|
|
}
|
|
}
|
|
|
|
// 10. cut all red wires if there are no more than 2 wires of the same color
|
|
if (max_len <= 2) {
|
|
for (int i = 0; i < red_pos_len; i++) {
|
|
out_cut[red_pos[i]] = true;
|
|
ESP_LOGI(TAG, "10. cutting %d", red_pos[i]);
|
|
}
|
|
}
|
|
|
|
// 11. cut the last wire if it is the same color as the first wire
|
|
if (wires[0] == wires[NUM_WIRES-1]) {
|
|
out_cut[NUM_WIRES-1] = true;
|
|
ESP_LOGI(TAG, "11. cutting %d", NUM_WIRES-1);
|
|
}
|
|
|
|
// 12. cut any wire adjacent to both a yellow and blue wire
|
|
for (int i = 0; i < NUM_WIRES-2; i++) {
|
|
if (
|
|
(wires[i] == WireColor::yellow && wires[i+2] == WireColor::blue) ||
|
|
(wires[i] == WireColor::blue && wires[i+2] == WireColor::white)
|
|
) {
|
|
out_cut[i+1] = true;
|
|
ESP_LOGI(TAG, "12. cutting %d", i+1);
|
|
}
|
|
}
|
|
|
|
// NEVER CUT
|
|
|
|
// never cut blue wires next to red or green wires
|
|
for (int i = 0; i < blue_pos_len; i++) {
|
|
int pos = blue_pos[i];
|
|
if (
|
|
wires[pos-1] == WireColor::red ||
|
|
wires[pos-1] == WireColor::green ||
|
|
wires[pos+1] == WireColor::red ||
|
|
wires[pos+1] == WireColor::green
|
|
) {
|
|
out_cut[pos] = false;
|
|
}
|
|
}
|
|
|
|
// never cut white wires if there is at least one red, black and green wire
|
|
if (red_pos_len > 0 && green_pos_len > 0 && black_pos_len > 0) {
|
|
for (int i = 0; i < white_pos_len; i++) {
|
|
out_cut[white_pos[i]] = false;
|
|
}
|
|
}
|
|
|
|
// never cut red or black wires in the 4th or 7th positions
|
|
if (wires[3] == WireColor::red || wires[3] == WireColor::black) {
|
|
out_cut[3] = false;
|
|
}
|
|
if (wires[6] == WireColor::red || wires[6] == WireColor::black) {
|
|
out_cut[6] = false;
|
|
}
|
|
|
|
// never cut wires that have the same color on both sides of it
|
|
for (int i = 0; i < NUM_WIRES-2; i++) {
|
|
if (wires[i] == wires[i+2]) {
|
|
out_cut[i+1] = false;
|
|
}
|
|
}
|
|
|
|
// never cut a wire in the 1st, 2nd, or 3rd position if it is the same color as the wire in the 4th position.
|
|
if (wires[0] == wires[3]) {
|
|
out_cut[0] = false;
|
|
}
|
|
if (wires[1] == wires[3]) {
|
|
out_cut[1] = false;
|
|
}
|
|
if (wires[2] == wires[3]) {
|
|
out_cut[2] = false;
|
|
}
|
|
|
|
// never cut a blue or green wire in the 8th postion
|
|
if (wires[7] == WireColor::blue || wires[7] == WireColor::green) {
|
|
out_cut[7] = false;
|
|
}
|
|
|
|
// never cut a wire in the 5th position if there are no yellow wires
|
|
if (yellow_pos_len == 0) {
|
|
out_cut[4] = false;
|
|
}
|
|
}
|
|
|
|
static int max(int n0, int n1, int n2, int n3, int n4, int n5) {
|
|
int max = n0;
|
|
|
|
if (n1 > max) max = n1;
|
|
if (n2 > max) max = n2;
|
|
if (n3 > max) max = n3;
|
|
if (n4 > max) max = n4;
|
|
if (n5 > max) max = n5;
|
|
|
|
return max;
|
|
}
|