#include #include #include #include #include #include #include #include #include #include #include using namespace std; constexpr int LEN = 5, N = 100'000; constexpr size_t num_primes = 8363; vector words; // int pre_feedback[num_primes][num_primes]; int feedback(string guess, string sol) { // answer = base-4 number of 5 colours white (not present), yellow (at other position) and green (correct) int col[5], present[10]; memset(present, 0, sizeof present); memset(col, 0, sizeof col); for (int i = 0; i < LEN; i++) { if (sol[i] == guess[i]) { col[i] = 2; } else { present[ sol[i] - '0' ]++; } } for (int i = 0; i < LEN; i++) { if (sol[i] != guess[i] && present[ guess[i]-'0' ] > 0) { col[i] = 1; present[ guess[i]-'0' ]--; } } return (col[0] << 0) | (col[1] << 2) | (col[2] << 4) | (col[3] << 6) | (col[4] << 8); } #define FEEDBACK(i, j) feedback(words[i], words[j]) int result_to_int(const char *ch) { return *ch ? ( *ch == 'g' ? 2 : (*ch == 'y') ) | result_to_int(ch + 1)<<2 : 0; } void gen_words() { bitset is_prime; is_prime.set(); for (int i = 2; i * i < N; i++) if (is_prime.test(i)) for (int j = i*i; j < N; j += i) is_prime.reset(j); // only output primes in range [N/10, N] for (int i = N/10; i < N; i++) if (is_prime[i]) { stringstream ss; ss << i; words.push_back(ss.str()); } assert(words.size() == num_primes); /* for (int i = 0; i < (int) num_primes; i++) for (int j = 0; j < (int) num_primes; j++) pre_feedback[i][j] = feedback(words[i], words[j]); */ } int best_guess(vector &candidates) { assert(!candidates.empty()); if (candidates.size() <= 2) return candidates[0]; // int min_max = 1<<30; float best = 0.0; int attain = -1; if (candidates.size() <= 10) { for (int guess = 0; guess < (int) num_primes; guess++) { unordered_map nr_cands; for (int sol : candidates) { int res = FEEDBACK(guess, sol); nr_cands[res]++; } float alt = 0; for (auto [k, v] : nr_cands) alt += v * log2(v); if (attain == -1 || alt < best) { best = alt; attain = guess; } } } else { for (int guess : candidates) { unordered_map nr_cands; for (int sol : candidates) { int res = FEEDBACK(guess, sol); nr_cands[res]++; } float alt = 0; for (auto [k, v] : nr_cands) alt += v * log2(v); if (attain == -1 || alt < best) { best = alt; attain = guess; } } } assert(attain != -1); return attain; } int main() { int num_testcases; const char good[6] = "ggggg"; string query0 = "12583", query1 = "40697"; char result0[6], result1[6], result[6]; gen_words(); scanf("%d", &num_testcases); while (num_testcases --> 0) { printf("%s\n", query0.c_str()); fflush(stdout); scanf("%s", result0); if (strcmp(result0, good) == 0) continue; printf("%s\n", query1.c_str()); fflush(stdout); scanf("%s", result1); if (strcmp(result1, good) == 0) continue; int res0 = result_to_int(result0); int res1 = result_to_int(result1); vector candidates; for (int i = 0; i < (int) num_primes; i++) { if (feedback(query0, words[i]) != res0) continue; if (feedback(query1, words[i]) != res1) continue; candidates.push_back(i); } while (true) { // make the best guess int query = best_guess(candidates); printf("%s\n", words[query].c_str()); fflush(stdout); scanf("%s", result); if (strcmp(result, good) == 0) break; int res = result_to_int(result), ncandidates = 0; for (int i = 0; i < (int) candidates.size(); i++) { if (FEEDBACK(query, candidates[i]) == res) { candidates[ ncandidates++ ] = candidates[i]; } } candidates.resize(ncandidates); // fprintf(stderr, "Candidates left: %zu\n", candidates.size()); /* if (candidates.size() <= 10) { for (int s : candidates) fprintf(stderr, "%s ", words[s].c_str()); fprintf(stderr, "\n"); } */ } } return 0; }