#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; }