From aab2829c9c686f9207974fe080da953ee3a8e34b Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 3 Jun 2018 02:59:27 -0700 Subject: [PATCH 1/5] --- tennis.c | 247 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 tennis.c diff --git a/tennis.c b/tennis.c new file mode 100644 index 0000000..fc56713 --- /dev/null +++ b/tennis.c @@ -0,0 +1,247 @@ +// arbitrary terminology i made up on the spot: +// "entry" = code as exposed to user, in ASCII. +// "code" = code as indices into lut, used internally. + +#include +#include +#include +#include + +typedef uint32_t u32; +typedef uint64_t u64; + +#define lament(...) fprintf(stderr, __VA_ARGS__) +#define sizeofel(a) (sizeof(*(a))) +#define LEN(a) (sizeof(a) / sizeofel(a)) + +const char *lut[] = { + "N", "P", "Q", "R", + "T", "W", "X", "Y", + "17I", "2Z", "5S", "8B", + "0OD", "UV", "3", "4", + "6", "9", "A", "C", + "E", "F", "G", "H", + "J", "K", "L", "M", +}; + +enum {lutlen = LEN(lut)}; // should be 28. + +const u32 c0 = 64; +const u32 c1 = 11; +const u32 c2 = 21; +const u32 c3 = 2; +const u32 c4 = 21; +const u32 c5 = 8; + +typedef struct { + char code[9]; + // data[0]: cup name + // data[1]: ? + // data[2]: player character (-1 for player's choice) + // data[3]: game mode? (boolean) + // data[4]: opponent character (-1 for player's choice) + // data[5]: ? + // data[6]: court (-1 for player's choice) + u32 data[7]; +} code_t; + +code_t translate(const char *entry) { + code_t code = {0}; + for (int i = 0; i < 9; i++) { + char e = entry[i]; + if (e == '\0') return code; + for (int j = 0; j < lutlen; j++) { + const char *c = lut[j]; + while (*c != '\0') { + if (e == *c) { + code.code[i] = j; + goto nextchar; + } + c++; + } + } + nextchar: + ; + } + return code; +} + +int check_ranges(code_t *code) { + int within_range = 1; + within_range &= code->data[0] < c0; + within_range &= code->data[1] < c1; + within_range &= code->data[2] + 1 < c2 - 4; + within_range &= code->data[3] < c3; + within_range &= code->data[4] + 1 < c2 - 4; + within_range &= code->data[5] < c5; + within_range &= code->data[6] + 1 < 6; + return within_range; +} + +int decode_data(code_t *code) { + u32 part0 = 0, part1 = 0; + for (int i = 5; i >= 0; i--) part0 = part0 * lutlen + code->code[i]; + for (int i = 8; i >= 6; i--) part1 = part1 * lutlen + code->code[i]; + u32 checksum = (part0 + 0x2AE0) % (lutlen * lutlen * lutlen); + +#if !NDEBUG + lament("merged data: %08X\n", part0); + lament("expected checksum: %08X\n", part1); + lament("actual checksum: %08X\n", checksum); +#endif + + u32 leftover = part0 ^ 0x02AAAAAA; + + code->data[0] = leftover % c0; leftover /= c0; + code->data[1] = leftover % c1; leftover /= c1; + code->data[2] = leftover % c2; leftover /= c2; + code->data[3] = leftover % c3; leftover /= c3; + code->data[4] = leftover % c4; leftover /= c4; + code->data[5] = leftover % c5; leftover /= c5; + code->data[6] = leftover; + + code->data[2]--; + code->data[4]--; + code->data[6]--; + +#if !NDEBUG + lament("stored data:"); + for (int i = 0; i < 7; i++) lament(" %i", code->data[i]); + lament("\n"); +#endif + + if (checksum != part1) return 0; + + if (!check_ranges(code)) return 0; + + const u32 neg1 = (u32)-1; + if (code->data[2] == neg1 || code->data[2] != code->data[4]); else return 0; + if (code->data[1] < 7 || (code->data[2] != neg1 && code->data[4] != neg1)); else return 0; + + return 1; +} + +int encode_data(code_t *code) { + const u32 neg1 = (u32)-1; + if (code->data[2] == neg1 || code->data[2] != code->data[4]); else return 0; + if (code->data[1] < 7 || (code->data[2] != neg1 && code->data[4] != neg1)); else return 0; + + if (!check_ranges(code)) return 0; + + u32 combined = 0; + combined = code->data[6] + 1; + combined = combined * c5 + code->data[5]; + combined = combined * c4 + code->data[4] + 1; + combined = combined * c3 + code->data[3]; + combined = combined * c2 + code->data[2] + 1; + combined = combined * c1 + code->data[1]; + combined = combined * c0 + code->data[0]; + combined = combined ^ 0x02AAAAAA; + + u32 checksum = (combined + 0x2AE0) % (lutlen * lutlen * lutlen); + +#if !NDEBUG + lament("merged data: %08X\n", combined); + lament("checksum: %08X\n", checksum); +#endif + + u32 part0 = combined, part1 = checksum; + for (int i = 0; i <= 5; i++) { + code->code[i] = part0 % lutlen; part0 /= lutlen; + } + for (int i = 6; i <= 8; i++) { + code->code[i] = part1 % lutlen; part1 /= lutlen; + } + + return 1; +} + +int validate_entry(const char *entry) { + code_t code = translate(entry); + +#if !NDEBUG + lament("character mapping:"); + for (int i = 0; i < 9; i++) { + lament(" %c->%i,", entry[i], code.code[i]); + } + lament("\n"); +#endif + + return decode_data(&code); +} + +static u64 prng_state = 1; +static u32 prng() { + prng_state = 3935559000370003845 * prng_state + 1; + return prng_state ^ (prng_state >> 32); +} + +int bruteforce() { // with a fixed seed, this is useful for testing. + int found = 0; + code_t code = {0}; + while (found < 10) { + for (int j = 0; j < 9; j++) { + code.code[j] = prng() % lutlen; + } + if (decode_data(&code)) { + found++; + for (int j = 0; j < 9; j++) { + int index = code.code[j]; + printf("%c", lut[index][0]); + } + printf("\n"); + } + } + return 1; +} + +int main(int argc, char **argv) { + if (argc == 1) { + // self-test. + if (!validate_entry("48HWOR482")) return -1; + if (!validate_entry("5G3LTQ5GN")) return -1; + if (!validate_entry("A3W5KQA3C")) return -1; + if (!validate_entry("ARM6JQARU")) return -1; + if (!validate_entry("E8ULJRE8M")) return -1; + if (!validate_entry("J6M9PQJ6U")) return -1; + if (!validate_entry("LA98JRLAR")) return -1; + if (!validate_entry("M1C2YQM1W")) return -1; + if (!validate_entry("MM55MQMMJ")) return -1; + if (!validate_entry("N24K8QN2P")) return -1; + if (!validate_entry("OF9XFQOFR")) return -1; + if (!validate_entry("UOUFMPUOM")) return -1; + if (!validate_entry("V2UFMPUZM")) return -1; + return 0; + } + + code_t code = {0}; + int code_i = 0; + + int invalid_count = 0; + for (int i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (strlen(arg) == 9) { + invalid_count += !validate_entry(arg); + } else if (!strcmp(arg, "bruteforce")) { + invalid_count += !bruteforce(); + } else { + u32 datum = strtoul(arg, NULL, 0); + code.data[code_i] = datum; + code_i++; + + if (code_i >= 7) { + code_i = 0; + if (encode_data(&code)) { + for (int j = 0; j < 9; j++) { + int index = code.code[j]; + printf("%c", lut[index][0]); + } + printf("\n"); + } else { + invalid_count++; + } + } + } + } + return invalid_count; +} From f4c682bbea2678ac0df3adb48eaf0c89dba6f9e1 Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 3 Jun 2018 03:36:00 -0700 Subject: [PATCH 2/5] --- tennis.c | 53 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/tennis.c b/tennis.c index fc56713..3148841 100644 --- a/tennis.c +++ b/tennis.c @@ -15,6 +15,9 @@ typedef uint64_t u64; #define LEN(a) (sizeof(a) / sizeofel(a)) const char *lut[] = { + // characters within the same string are considered the same. + // that means the number of choices for a single character + // goes down from 36 to 28. "N", "P", "Q", "R", "T", "W", "X", "Y", "17I", "2Z", "5S", "8B", @@ -114,6 +117,7 @@ int decode_data(code_t *code) { if (!check_ranges(code)) return 0; + // TODO: refactor so this isn't repeated in encode_data. const u32 neg1 = (u32)-1; if (code->data[2] == neg1 || code->data[2] != code->data[4]); else return 0; if (code->data[1] < 7 || (code->data[2] != neg1 && code->data[4] != neg1)); else return 0; @@ -197,20 +201,41 @@ int bruteforce() { // with a fixed seed, this is useful for testing. int main(int argc, char **argv) { if (argc == 1) { - // self-test. - if (!validate_entry("48HWOR482")) return -1; - if (!validate_entry("5G3LTQ5GN")) return -1; - if (!validate_entry("A3W5KQA3C")) return -1; - if (!validate_entry("ARM6JQARU")) return -1; - if (!validate_entry("E8ULJRE8M")) return -1; - if (!validate_entry("J6M9PQJ6U")) return -1; - if (!validate_entry("LA98JRLAR")) return -1; - if (!validate_entry("M1C2YQM1W")) return -1; - if (!validate_entry("MM55MQMMJ")) return -1; - if (!validate_entry("N24K8QN2P")) return -1; - if (!validate_entry("OF9XFQOFR")) return -1; - if (!validate_entry("UOUFMPUOM")) return -1; - if (!validate_entry("V2UFMPUZM")) return -1; + // self-test with codes known before this program was written. +#define TEST(entry) if (!validate_entry(entry)) return -1 + TEST("48HWOR482"); + TEST("5G3LTQ5GN"); + //TEST("5PURGR5PM"); + //TEST("6PJEGRGP5"); + //TEST("8GYUHR86F"); + TEST("A3W5KQA3C"); + TEST("ARM6JQARU"); + TEST("E880MPE8K"); + TEST("E8ULJRE8M"); + TEST("EPJEGREP5"); + TEST("GH4KNQGHP"); + TEST("H6L3MPH60"); + TEST("HH4KNQHHP"); + TEST("J6M9PQJ6U"); + TEST("JEP8YQJE4"); + TEST("LA98JRLAR"); + TEST("LQM1MPLQU"); + TEST("LTHWYQLT2"); + TEST("M1C2YQM1W"); + TEST("MM55MQMMJ"); + TEST("N24K8QN2P"); + TEST("OF9XFQOFR"); + TEST("P4K6GRP48"); + TEST("TE6WARTEQ"); + TEST("TQJEGRTQ5"); + TEST("UOUFMPUOM"); + TEST("V2UFMPUZM"); + TEST("W2HEGRW22"); + TEST("WQJEGRWQ5"); + TEST("WRWQARWRC"); + TEST("YQJEGRYQ5"); + //TEST("ZKZLZKNNR"); +#undef TEST return 0; } From 22e3eceb5dd59d1651574fd2fb6435264bcc8213 Mon Sep 17 00:00:00 2001 From: Connor Date: Sun, 3 Jun 2018 13:00:46 -0700 Subject: [PATCH 3/5] --- tennis.c | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tennis.c b/tennis.c index 3148841..fee55a7 100644 --- a/tennis.c +++ b/tennis.c @@ -174,6 +174,14 @@ int validate_entry(const char *entry) { return decode_data(&code); } +static void print_code(code_t *code) { + for (int j = 0; j < 9; j++) { + int index = code->code[j]; + printf("%c", lut[index][0]); + } + printf("\n"); +} + static u64 prng_state = 1; static u32 prng() { prng_state = 3935559000370003845 * prng_state + 1; @@ -189,25 +197,19 @@ int bruteforce() { // with a fixed seed, this is useful for testing. } if (decode_data(&code)) { found++; - for (int j = 0; j < 9; j++) { - int index = code.code[j]; - printf("%c", lut[index][0]); - } - printf("\n"); + print_code(&code); } } return 1; } +#ifndef TENNIS_NO_MAIN int main(int argc, char **argv) { if (argc == 1) { // self-test with codes known before this program was written. #define TEST(entry) if (!validate_entry(entry)) return -1 TEST("48HWOR482"); TEST("5G3LTQ5GN"); - //TEST("5PURGR5PM"); - //TEST("6PJEGRGP5"); - //TEST("8GYUHR86F"); TEST("A3W5KQA3C"); TEST("ARM6JQARU"); TEST("E880MPE8K"); @@ -234,7 +236,6 @@ int main(int argc, char **argv) { TEST("WQJEGRWQ5"); TEST("WRWQARWRC"); TEST("YQJEGRYQ5"); - //TEST("ZKZLZKNNR"); #undef TEST return 0; } @@ -246,6 +247,7 @@ int main(int argc, char **argv) { for (int i = 1; i < argc; i++) { const char *arg = argv[i]; if (strlen(arg) == 9) { + //if (validate_entry(arg)) printf("%s\n", arg); invalid_count += !validate_entry(arg); } else if (!strcmp(arg, "bruteforce")) { invalid_count += !bruteforce(); @@ -257,11 +259,7 @@ int main(int argc, char **argv) { if (code_i >= 7) { code_i = 0; if (encode_data(&code)) { - for (int j = 0; j < 9; j++) { - int index = code.code[j]; - printf("%c", lut[index][0]); - } - printf("\n"); + print_code(&code); } else { invalid_count++; } @@ -270,3 +268,4 @@ int main(int argc, char **argv) { } return invalid_count; } +#endif From cd0b5a8b4b25a7363c1f867c2b362e987b7c0734 Mon Sep 17 00:00:00 2001 From: Connor Date: Mon, 4 Jun 2018 11:07:29 -0700 Subject: [PATCH 4/5] --- tennis.c | 139 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 94 insertions(+), 45 deletions(-) diff --git a/tennis.c b/tennis.c index fee55a7..96c79ec 100644 --- a/tennis.c +++ b/tennis.c @@ -27,23 +27,63 @@ const char *lut[] = { "J", "K", "L", "M", }; -enum {lutlen = LEN(lut)}; // should be 28. +enum {lutlen = LEN(lut)}; // should be 28. -const u32 c0 = 64; -const u32 c1 = 11; -const u32 c2 = 21; -const u32 c3 = 2; -const u32 c4 = 21; -const u32 c5 = 8; +const u32 bases[] = { + 64, + 11, + 21, + 2, + 21, + 8, + 6, // dummy value. +}; + +enum { + MT_CHOICE = -1, +}; + +typedef enum { + MT_YOSHI, + MT_PEACH, + MT_MARIO, + MT_BOWSER, + MT_BOO, + MT_DK, + MT_BABY_MARIO, + MT_TOAD, + MT_WALUIGI, + MT_WARIO, + MT_LUIGI, + MT_DAISY, + MT_BIRDO, + MT_SHYGUY, + MT_DK_JR, + MT_PARATROOPA, +} character_t; + +typedef enum { + MT_SINGLES_GAME, + MT_SINGLES_TIME_3, + MT_SINGLES_TIME_5, + MT_SINGLES_BALL_1, + MT_SINGLES_BALL_3, + MT_SINGLES_POINT_3, + MT_SINGLES_POINT_5, + MT_DOUBLES_GAME_1, + MT_DOUBLES_TIME_3, + MT_DOUBLES_BALL_1, + MT_DOUBLES_POINT_3, +} gamemode_t; typedef struct { char code[9]; // data[0]: cup name - // data[1]: ? + // data[1]: gamemode // data[2]: player character (-1 for player's choice) - // data[3]: game mode? (boolean) + // data[3]: unknown (boolean) // data[4]: opponent character (-1 for player's choice) - // data[5]: ? + // data[5]: unknown // data[6]: court (-1 for player's choice) u32 data[7]; } code_t; @@ -69,16 +109,20 @@ code_t translate(const char *entry) { return code; } -int check_ranges(code_t *code) { - int within_range = 1; - within_range &= code->data[0] < c0; - within_range &= code->data[1] < c1; - within_range &= code->data[2] + 1 < c2 - 4; - within_range &= code->data[3] < c3; - within_range &= code->data[4] + 1 < c2 - 4; - within_range &= code->data[5] < c5; - within_range &= code->data[6] + 1 < 6; - return within_range; +int check_ranges(const code_t *code) { + const u32 neg1 = (u32)-1; + const u32 *data = code->data; + + return + (data[0] < bases[0]) && + (data[1] < bases[1]) && + (data[2] + 1 < bases[2] - 4) && + (data[3] < bases[3]) && + (data[4] + 1 < bases[4] - 4) && + (data[5] < bases[5]) && + (data[6] + 1 < bases[6]) && + (data[2] == neg1 || data[2] != data[4]) && + (data[1] < 7 || (data[2] != neg1 && data[4] != neg1)); } int decode_data(code_t *code) { @@ -95,12 +139,12 @@ int decode_data(code_t *code) { u32 leftover = part0 ^ 0x02AAAAAA; - code->data[0] = leftover % c0; leftover /= c0; - code->data[1] = leftover % c1; leftover /= c1; - code->data[2] = leftover % c2; leftover /= c2; - code->data[3] = leftover % c3; leftover /= c3; - code->data[4] = leftover % c4; leftover /= c4; - code->data[5] = leftover % c5; leftover /= c5; + code->data[0] = leftover % bases[0]; leftover /= bases[0]; + code->data[1] = leftover % bases[1]; leftover /= bases[1]; + code->data[2] = leftover % bases[2]; leftover /= bases[2]; + code->data[3] = leftover % bases[3]; leftover /= bases[3]; + code->data[4] = leftover % bases[4]; leftover /= bases[4]; + code->data[5] = leftover % bases[5]; leftover /= bases[5]; code->data[6] = leftover; code->data[2]--; @@ -117,29 +161,20 @@ int decode_data(code_t *code) { if (!check_ranges(code)) return 0; - // TODO: refactor so this isn't repeated in encode_data. - const u32 neg1 = (u32)-1; - if (code->data[2] == neg1 || code->data[2] != code->data[4]); else return 0; - if (code->data[1] < 7 || (code->data[2] != neg1 && code->data[4] != neg1)); else return 0; - return 1; } int encode_data(code_t *code) { - const u32 neg1 = (u32)-1; - if (code->data[2] == neg1 || code->data[2] != code->data[4]); else return 0; - if (code->data[1] < 7 || (code->data[2] != neg1 && code->data[4] != neg1)); else return 0; - if (!check_ranges(code)) return 0; u32 combined = 0; combined = code->data[6] + 1; - combined = combined * c5 + code->data[5]; - combined = combined * c4 + code->data[4] + 1; - combined = combined * c3 + code->data[3]; - combined = combined * c2 + code->data[2] + 1; - combined = combined * c1 + code->data[1]; - combined = combined * c0 + code->data[0]; + combined = combined * bases[5] + code->data[5]; + combined = combined * bases[4] + code->data[4] + 1; + combined = combined * bases[3] + code->data[3]; + combined = combined * bases[2] + code->data[2] + 1; + combined = combined * bases[1] + code->data[1]; + combined = combined * bases[0] + code->data[0]; combined = combined ^ 0x02AAAAAA; u32 checksum = (combined + 0x2AE0) % (lutlen * lutlen * lutlen); @@ -174,7 +209,7 @@ int validate_entry(const char *entry) { return decode_data(&code); } -static void print_code(code_t *code) { +static void print_code(const code_t *code) { for (int j = 0; j < 9; j++) { int index = code->code[j]; printf("%c", lut[index][0]); @@ -188,10 +223,24 @@ static u32 prng() { return prng_state ^ (prng_state >> 32); } -int bruteforce() { // with a fixed seed, this is useful for testing. +int bruteforce(int count) { + /* with a fixed seed, this is useful for testing. + the first 10 results should be: + 8RPEHR8R4 + MNXKWQMNE + CE922RCER + MQ1KMQMQG + 2N9GGR2NR + 25L53R250 + 15R09R159 + 9R9LMQ9RR + L312MQL3G + P93M6QP9N + */ + int found = 0; code_t code = {0}; - while (found < 10) { + while (found < count) { for (int j = 0; j < 9; j++) { code.code[j] = prng() % lutlen; } @@ -250,7 +299,7 @@ int main(int argc, char **argv) { //if (validate_entry(arg)) printf("%s\n", arg); invalid_count += !validate_entry(arg); } else if (!strcmp(arg, "bruteforce")) { - invalid_count += !bruteforce(); + invalid_count += !bruteforce(10); } else { u32 datum = strtoul(arg, NULL, 0); code.data[code_i] = datum; From 50b1e9a48eac8667799222a404d60dbbc0963d35 Mon Sep 17 00:00:00 2001 From: Connor Date: Mon, 4 Jun 2018 11:14:04 -0700 Subject: [PATCH 5/5] --- tennis.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/tennis.c b/tennis.c index 96c79ec..dce7a93 100644 --- a/tennis.c +++ b/tennis.c @@ -76,6 +76,14 @@ typedef enum { MT_DOUBLES_POINT_3, } gamemode_t; +typedef enum { + MT_HARD, + MT_CLAY, + MT_GRASS, + MT_COMPOSITION, + MT_OPEN, +} court_t; + typedef struct { char code[9]; // data[0]: cup name @@ -110,7 +118,7 @@ code_t translate(const char *entry) { } int check_ranges(const code_t *code) { - const u32 neg1 = (u32)-1; + const u32 choice = (u32)MT_CHOICE; const u32 *data = code->data; return @@ -121,8 +129,8 @@ int check_ranges(const code_t *code) { (data[4] + 1 < bases[4] - 4) && (data[5] < bases[5]) && (data[6] + 1 < bases[6]) && - (data[2] == neg1 || data[2] != data[4]) && - (data[1] < 7 || (data[2] != neg1 && data[4] != neg1)); + (data[2] == choice || data[2] != data[4]) && + (data[1] < 7 || (data[2] != choice && data[4] != choice)); } int decode_data(code_t *code) {