Last active
January 19, 2025 02:33
-
-
Save PardhavMaradani/8736c25cf70e36292204ec27058c213f to your computer and use it in GitHub Desktop.
My CodeCup 2025 submission
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <algorithm> | |
| #include <cstring> | |
| #include <climits> | |
| #include <vector> | |
| #include <map> | |
| #include <set> | |
| #include <bitset> | |
| using namespace std; | |
| #define DEBUG 1 | |
| #define LOG(msg, ...) { \ | |
| if (DEBUG) { \ | |
| fprintf(stderr, "" msg "\n", ##__VA_ARGS__); \ | |
| } \ | |
| } | |
| typedef unsigned char u8; | |
| typedef pair<u8, u8> u8pair; | |
| typedef struct { | |
| u8 a[3]; | |
| } u8a3_t; | |
| typedef struct { | |
| u8 a[4]; | |
| } u8a4_t; | |
| typedef struct { | |
| vector <u8a3_t> a[2]; | |
| } tilev_t; | |
| typedef struct { | |
| double sqScore; | |
| double trScore; | |
| double liScore; | |
| u8 nSquares; | |
| u8 nTriangles; | |
| u8 nLines; | |
| } sscore_t; | |
| typedef pair<int, double> spair_t; | |
| typedef struct { | |
| u8 color; | |
| bitset <12> o[2]; | |
| vector <u8a4_t> sc; | |
| } cell_t; | |
| cell_t cells[16][20]; | |
| set <u8pair> ccells[7]; // 0 - ignore, colors 1 - 6 | |
| typedef struct { | |
| map <set<u8pair>, u8> vbox; | |
| } cstate_t; | |
| cstate_t cstate[7]; | |
| map <pair<int, int>, vector<int>> xMap = { | |
| {{ 0, 1}, {-1, 1, -1, 0, 1, 1, 1, 0}}, | |
| {{-1, 1}, {-1, 0, 0, 1}}, | |
| {{-1, 0}, {-1, -1, 0, -1, -1, 1, 0, 1}}, | |
| {{-1, -1}, {-1, 0, 0, -1}}, | |
| {{ 0, -1}, {-1, -1, -1, 0, 1, -1, 1, 0}}, | |
| {{ 1, -1}, {0, -1, 1, 0}}, | |
| {{ 1, 0}, {1, -1, 0, -1, 1, 1, 0, 1}}, | |
| {{ 1, 1}, {1, 0, 0, 1}} | |
| }; | |
| int secret = 0; | |
| bool player1 = false; | |
| typedef struct { | |
| u8 r; | |
| u8 c; | |
| bool d; // 0 - h, 1 - v | |
| double score[7]; // 0 - ignore, colors 1 - 6 | |
| double e; | |
| sscore_t s[7]; | |
| } move_t; | |
| u8 rSQ[7] = {0, 0, 0, 0, 0, 0, 0}; | |
| u8 maxOCSquares = 0; | |
| inline bool inBoard(int r, int c) { | |
| return (r >= 0 && r < 16 && c >= 0 && c < 20); | |
| } | |
| inline bool hasOverlap(int r, int c, u8 d) { | |
| return inBoard(r, c) && cells[r][c].o[d].count() > 0; | |
| } | |
| int findValidMoves(move_t moves[512]) { | |
| int nMoves = 0; | |
| for (u8 d = 0; d < 2; d++) { | |
| u8 mr = (d == 0) ? 15 : 11; | |
| u8 mc = (d == 0) ? 15 : 19; | |
| for (u8 r = 0; r < mr; r++) { | |
| for (u8 c = 0; c < mc; c++) { | |
| u8 oc = cells[r][c].o[d].count(); | |
| if (oc == 0) { | |
| // check adjacency | |
| if (hasOverlap(r - 1, c, d) || hasOverlap(r + 1, c, d) || hasOverlap(r, c - 1, d) || hasOverlap(r, c + 1, d)) { | |
| moves[nMoves++] = {r, c, (bool)d}; | |
| } | |
| } else if (oc <= 4) { | |
| moves[nMoves++] = {r, c, (bool)d}; | |
| } | |
| } | |
| } | |
| } | |
| return nMoves; | |
| } | |
| u8 nDMoves(u8 r, u8 c) { | |
| u8 dMoves = 0; | |
| for (u8 i = 0; i < cells[r][c].sc.size(); i++) { | |
| u8 sr = cells[r][c].sc[i].a[0]; | |
| u8 sc = cells[r][c].sc[i].a[1]; | |
| u8 d = cells[r][c].sc[i].a[2]; | |
| if (cells[sr][sc].o[d].count() <= 4) { | |
| dMoves++; | |
| } | |
| } | |
| return dMoves; | |
| } | |
| u8 nDMoves(u8pair p) { | |
| return nDMoves(p.first, p.second); | |
| } | |
| void addVBoxes(u8 r, u8 c, u8 color, u8 length, u8 wr, u8 wc) { | |
| int rd = (wr - r) / length; | |
| int cd = (wc - c) / length; | |
| auto v = xMap[{rd, cd}]; | |
| for (u8 i = 0; i < v.size() / 4; i++) { | |
| int r1 = r + v[i * 4] * length; | |
| int c1 = c + v[i * 4 + 1] * length; | |
| int r2 = r + v[i * 4 + 2] * length; | |
| int c2 = c + v[i * 4 + 3] * length; | |
| if (inBoard(r1, c1) && inBoard(r2, c2)) { | |
| set <u8pair> vbox = {{r, c}, {wr, wc}, {r1, c1}, {r2, c2}}; | |
| if (cstate[color].vbox.find(vbox) == cstate[color].vbox.end()) { | |
| cstate[color].vbox[vbox] = length; | |
| } | |
| } | |
| } | |
| } | |
| void modifyColorCell(u8 r, u8 c, u8 color, int delta) { | |
| if (color == 0) { | |
| // update overlaps | |
| for (size_t i = 0; i < cells[r][c].sc.size(); i++) { | |
| u8 sr = cells[r][c].sc[i].a[0]; | |
| u8 sc = cells[r][c].sc[i].a[1]; | |
| u8 d = cells[r][c].sc[i].a[2]; | |
| u8 p = cells[r][c].sc[i].a[3]; | |
| if (delta == -1) { | |
| cells[sr][sc].o[d].set(p); | |
| } else { | |
| cells[r][c].color = color; | |
| cells[sr][sc].o[d].reset(p); | |
| } | |
| } | |
| return; | |
| } | |
| if (color == 7) { | |
| // temp transparent cell | |
| cells[r][c].color = (delta == 1) ? 7 : 0; | |
| return; | |
| } | |
| if (delta == 1) { | |
| cells[r][c].color = color; | |
| ccells[color].insert({r, c}); | |
| for (auto it = ccells[color].begin(); it != ccells[color].end(); it++) { | |
| u8 r1 = it->first; | |
| u8 c1 = it->second; | |
| if (r == r1 && c == c1) { | |
| continue; | |
| } | |
| if (r == r1 || c == c1 || abs(c - c1) == abs(r - r1)) { | |
| u8 length = max(abs(r - r1), abs(c - c1)); | |
| if (length > 15) { | |
| continue; | |
| } | |
| addVBoxes(r, c, color, length, r1, c1); | |
| } | |
| } | |
| } else { | |
| cells[r][c].color = 0; | |
| ccells[color].erase({r, c}); | |
| } | |
| } | |
| inline void addColorCell(u8 r, u8 c, u8 color) { | |
| modifyColorCell(r, c, color, 1); | |
| } | |
| inline void deleteColorCell(u8 r, u8 c, u8 color) { | |
| modifyColorCell(r, c, color, -1); | |
| } | |
| inline void getCellVecs(u8 r, u8 c, u8 color, tilev_t &v, bool transparent = false) { | |
| u8 ccolor = cells[r][c].color; | |
| if (ccolor == color) { | |
| return; | |
| } | |
| if (transparent && ccolor != 0) { | |
| return; | |
| } | |
| v.a[0].push_back({r, c, ccolor}); | |
| v.a[1].push_back({r, c, color}); | |
| } | |
| inline void getTileVecs(const char *tile, tilev_t &v, bool transparent = false) { | |
| u8 r = tile[0] - 'A'; | |
| u8 c = tile[1] - 'a'; | |
| for (u8 i = 0; i < 6; i++) { | |
| u8 color = tile[2 + i] - '0'; | |
| if (tile[8] == 'h') { | |
| getCellVecs(r, c + i, color, v, transparent); | |
| getCellVecs(r + 1, c + 5 - i, color, v, transparent); | |
| } else { | |
| getCellVecs(r + i, c + 1, color, v, transparent); | |
| getCellVecs(r + 5 - i, c, color, v, transparent); | |
| } | |
| } | |
| } | |
| tilev_t placeTileInCells(const char *tile, bool transparent = false) { // Eg: Hh216534h | |
| tilev_t v; | |
| getTileVecs(tile, v, transparent); | |
| vector <u8a3_t> del = v.a[0]; | |
| vector <u8a3_t> add = v.a[1]; | |
| for (size_t i = 0; i < del.size(); i++) { | |
| deleteColorCell(del[i].a[0], del[i].a[1], del[i].a[2]); | |
| } | |
| for (size_t i = 0; i < add.size(); i++) { | |
| addColorCell(add[i].a[0], add[i].a[1], add[i].a[2]); | |
| } | |
| return v; | |
| } | |
| void undoPlaceTileInCells(tilev_t v) { | |
| vector <u8a3_t> del = v.a[1]; | |
| vector <u8a3_t> add = v.a[0]; | |
| for (size_t i = 0; i < del.size(); i++) { | |
| deleteColorCell(del[i].a[0], del[i].a[1], del[i].a[2]); | |
| } | |
| for (size_t i = 0; i < add.size(); i++) { | |
| addColorCell(add[i].a[0], add[i].a[1], add[i].a[2]); | |
| } | |
| } | |
| #define DF(n, tn) ((tn == 0) ? 1.0 : (1.0 - ((double) n / (double) tn))) | |
| inline sscore_t shapeScores(u8 color, int nNmoves) { | |
| sscore_t scores = {0, 0, 0, 0, 0, 0}; | |
| vector <set <u8pair>> prune; | |
| for (auto it = cstate[color].vbox.begin(); it != cstate[color].vbox.end(); it++) { | |
| set <u8pair> vbox = it->first; | |
| u8 length = it->second; | |
| u8 count = 0; | |
| double vscore = 0; | |
| vector <double> sameDFs; | |
| vector <double> missingDFs; | |
| for (auto it1 = vbox.begin(); it1 != vbox.end(); it1++) { | |
| u8 r = it1->first; | |
| u8 c = it1->second; | |
| u8 dMoves = nDMoves(r, c); | |
| double df = DF(dMoves, nNmoves); | |
| if (cells[r][c].color == color) { | |
| vscore += length * df; | |
| count++; | |
| sameDFs.push_back(df); | |
| } else { | |
| missingDFs.push_back(df); | |
| } | |
| } | |
| if (count == 4) { | |
| if (sameDFs[0] == 1 && sameDFs[1] == 1 && sameDFs[2] == 1 && sameDFs[3] == 1) { | |
| scores.sqScore += vscore * 2; | |
| scores.nSquares++; | |
| } else { | |
| if (color == secret) { | |
| } else { | |
| scores.sqScore += vscore; | |
| scores.nSquares++; | |
| } | |
| } | |
| } else if (count == 3) { | |
| if (missingDFs[0] != 1) { | |
| if (sameDFs[0] == 1 && sameDFs[1] == 1 && sameDFs[2] == 1) { | |
| scores.trScore += vscore * 2; | |
| scores.nTriangles++; | |
| } else { | |
| if (color == secret) { | |
| scores.trScore += vscore / 2; | |
| scores.nTriangles++; | |
| } else { | |
| scores.trScore += vscore; | |
| scores.nTriangles++; | |
| } | |
| } | |
| } | |
| } else if (count == 2) { | |
| if (missingDFs[0] != 1 && missingDFs[1] != 1) { | |
| if (sameDFs[0] == 1 && sameDFs[1] == 1) { | |
| scores.liScore += vscore * 2; | |
| scores.nLines++; | |
| } else { | |
| if (color == secret) { | |
| scores.liScore += vscore / 2; | |
| scores.nLines++; | |
| } else { | |
| scores.liScore += vscore; | |
| scores.nLines++; | |
| } | |
| } | |
| } | |
| } else { | |
| prune.push_back(vbox); | |
| } | |
| } | |
| for (u8 i = 0; i < prune.size(); i++) { | |
| cstate[color].vbox.erase(prune[i]); | |
| } | |
| return scores; | |
| } | |
| void evalMove(const char *tile, move_t *move) { | |
| tilev_t v1 = placeTileInCells(tile); | |
| move_t nextMoves[512]; | |
| int nNmoves = findValidMoves(nextMoves); | |
| for (u8 color = 1; color <= 6; color++) { | |
| sscore_t scores = shapeScores(color, nNmoves); | |
| move->score[color] = (100 * scores.sqScore) + (10 * scores.trScore) + (1 * scores.liScore); | |
| move->s[color] = scores; | |
| } | |
| move->e = 0; | |
| undoPlaceTileInCells(v1); | |
| } | |
| void weightedScoresMove(move_t moves[512], int nMoves, const char *input) { // Eg: 364521 | |
| double mm[7] = {INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX, INT_MAX}; | |
| mm[secret] = INT_MIN; | |
| int mmI[7] = {-1, -1, -1, -1, -1, -1, -1}; | |
| for (int i = 0; i < nMoves; i++) { | |
| for (u8 c = 1; c <= 6; c++) { | |
| if (c == secret) { | |
| if (moves[i].score[c] > mm[c]) { | |
| mm[c] = moves[i].score[c]; | |
| mmI[c] = i; | |
| } | |
| } else { | |
| if (moves[i].score[c] < mm[c]) { | |
| mm[c] = moves[i].score[c]; | |
| mmI[c] = i; | |
| } | |
| } | |
| } | |
| } | |
| for (int c = 1; c <= 6; c++) { | |
| move_t move = moves[mmI[c]]; | |
| LOG("c = %d, s = [%5.0f %5.0f %5.0f %5.0f %5.0f %5.0f], m = [%2d,%2d,%2d]", c, move.score[1], move.score[2], move.score[3], move.score[4], move.score[5], move.score[6], move.r, move.c, move.d); | |
| } | |
| bool inc[7] = {false, false, false, false, false, false, false}; | |
| inc[secret] = true; | |
| if (maxOCSquares > 0) { | |
| for (int c = 1; c <= 6; c++) { | |
| if (rSQ[c] >= maxOCSquares) { | |
| inc[c] = true; | |
| } | |
| } | |
| } else { | |
| for (int c = 1; c <= 6; c++) { | |
| inc[c] = true; | |
| } | |
| } | |
| LOG("inc = [%d %d %d %d %d %d]", inc[1], inc[2], inc[3], inc[4], inc[5], inc[6]); | |
| double me = INT_MAX; | |
| int meI = -1; | |
| for (int i = 0; i < nMoves; i++) { | |
| for (int c = 1; c <= 6; c++) { | |
| if (!inc[c]) continue; | |
| moves[i].e += (moves[i].score[c] - mm[c]) * (moves[i].score[c] - mm[c]); | |
| } | |
| if (moves[i].e < me) { | |
| me = moves[i].e; | |
| meI = i; | |
| } | |
| } | |
| move_t move = moves[meI]; | |
| LOG("inc c, s = [%5.0f %5.0f %5.0f %5.0f %5.0f %5.0f], m = [%2d,%2d,%2d]", move.score[1], move.score[2], move.score[3], move.score[4], move.score[5], move.score[6], move.r, move.c, move.d); | |
| char tile[10]; | |
| snprintf(tile, 10, "%c%c%s%c", move.r + 'A', move.c + 'a', input, move.d ? 'v' : 'h'); | |
| LOG("place my tile [%s]", tile); | |
| placeTileInCells(tile); | |
| char output[4]; | |
| snprintf(output, 4, "%c%c%c", move.r + 'A', move.c + 'a', move.d ? 'v' : 'h'); | |
| printf("%s\n", output); // output move | |
| fflush(stdout); | |
| } | |
| void findMove(const char *input) { // Eg: 364521 | |
| move_t moves[512]; | |
| int nMoves = findValidMoves(moves); | |
| if (nMoves == 0) { | |
| LOG("no more moves"); | |
| return; | |
| } | |
| for (int i = 0; i < nMoves; i++) { | |
| move_t *move = &moves[i]; | |
| char tile[10]; | |
| snprintf(tile, 10, "%c%c%s%c", move->r + 'A', move->c + 'a', input, move->d ? 'v' : 'h'); | |
| evalMove(tile, move); | |
| } | |
| return weightedScoresMove(moves, nMoves, input); | |
| } | |
| void addMove(const char *input) { // Eg: Hh216534h | |
| placeTileInCells(input); | |
| // update real squares for other colors | |
| for (u8 color = 1; color <= 6; color++) { | |
| sscore_t scores = shapeScores(color, 0); | |
| if (color != secret && scores.nSquares > 0) { | |
| rSQ[color] += scores.nSquares; | |
| if (rSQ[color] > maxOCSquares) { | |
| maxOCSquares = rSQ[color]; | |
| } | |
| } | |
| } | |
| } | |
| void init_cells() { | |
| for (u8 r = 0; r < 16; r++) { | |
| for (u8 c = 0; c < 20; c++) { | |
| for (u8 d = 0; d < 2; d++) { | |
| cells[r][c].o[d].reset(); | |
| } | |
| cells[r][c].color = 0; | |
| cells[r][c].sc.clear(); | |
| } | |
| } | |
| for (u8 d = 0; d < 2; d++) { | |
| u8 mr = (d == 0) ? 15 : 11; | |
| u8 mc = (d == 0) ? 15 : 19; | |
| for (u8 r = 0; r < mr; r++) { | |
| for (u8 c = 0; c < mc; c++) { | |
| for (u8 i = 0; i < 6; i++) { | |
| if (d == 0) { | |
| cells[r][c + i].sc.push_back({r, c, 0, i}); | |
| cells[r + 1][c + 5 - i].sc.push_back({r, c, 0, (u8)(6 + i)}); | |
| } else{ | |
| cells[r + i][c + 1].sc.push_back({r, c, 1, i}); | |
| cells[r + 5 - i][c].sc.push_back({r, c, 1, (u8)(6 + i)}); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| int main(int argc, char *argv[]) { | |
| init_cells(); | |
| if (scanf("%d", &secret)) {} // Eg: 3 | |
| LOG("secret = %d", secret); | |
| char input[10]; | |
| int len = 0; | |
| do { | |
| if (scanf("%s", input)) {} | |
| len = strlen(input); | |
| if (len == 9) { // Eg: Hh216534h | |
| LOG("place given tile [%s]", input); | |
| addMove(input); | |
| } else if (len == 6) { // Eg: 364521 | |
| LOG("find move for [%s]", input); | |
| findMove(input); | |
| } else if (len == 5) { // Start | |
| player1 = true; | |
| LOG("player 1"); | |
| } else if (len == 4) { // Quit | |
| LOG("Quit"); | |
| break; | |
| } else { | |
| LOG("invalid input [%s]", input); | |
| return 1; | |
| } | |
| } while (true); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment