# TestCode ``` // input [/. throw] check throw // [*/print] print all tile heat and sum // [*/acheck] check hand's heat and sum // [*/check 'tile_id'] to check specific tile's heat and sum #include <iostream> #include <string.h> using namespace std; #define initial_heat37 192 *2 #define initial_heat28 128 *2 #define initial_heat19 64 *2 bool CmdCompare(const char* CmdToCmp); //直接比對command_title bool StrCompare(char* context, const char* Compare); //比對字串 void SubContxt(char* subArr, char* mainArr, short begin, char target); //從指定位置開始分割字串直到指定字元 short CheckWind(char* wind); // 查看場、局風 void GameReset(void); // 重置紀錄 void HeatProcess(short, short); short GetType(short title_id); // 取得花色 short GetDigit(short title_id); // 取得數字 short GetID(short title_id); // 取得同牌id short RemoveID(short tile_id); // 取得牌種 bool inRange(short a, short x ,short b); void change_output(short title_id); // 轉換輸出樣貌 struct Gameinfo{ short PlayerID = 1, BWind = 1, SWind = 1, OpenAt = 1, Master = 1, Mcombo = 0; Gameinfo() {} }; short hand[23] = {0}; // 手牌最多到22張[5槓(20)+2] short hand_size = 0; // 手牌數 short table[474] = {0}; // 記錄所有牌的狀況 <tile_id , status> (-x = player x 的棄牌, 0 = 未知, x = player x 的手牌) short Allsum[48] = {4}; // 記錄所有牌種剩餘數量(還有幾張未被棄出) short heat[40][5]; // 記錄各家需求牌種(順子) short LastThrow = 0; // 最後一個棄牌 char command_title[20] = {0}; // 指令 char command_contxt[70] = {0}; // 指令參數 int main() { //initialize Gameinfo currentGame; GameReset(); while (true) { memset(command_title, 0, 20 * sizeof(char)); //sting.h 僅用 memset memset(command_contxt, 0, 70 * sizeof(char)); command_title[0] = ' ', command_contxt[0] = ' '; char inputchar; bool is_title = true; for (short title_size = 0, contxt_size = 0; title_size < 20 && contxt_size < 70;) { inputchar = cin.get(); //輸入形式必須為 "<command> <context>[Enter]" if (inputchar == '\n') { command_title[title_size] = 0; command_contxt[contxt_size] = 0; break; } if (!is_title) { command_contxt[contxt_size++] = inputchar; continue; } else if (is_title && inputchar == ' ') { is_title = false; continue; } else command_title[title_size++] = inputchar; } cout << "Recieved: [" << command_title << ' ' << command_contxt << ']' << endl; //////////////////////////////////////////////////////////////////////////// // 開始(input) switch (command_title[1]) //first letter from command { case 's': //start currentGame.PlayerID = (short)command_contxt[3] - '0'; GameReset(); break; // 本局資訊(input) case 'i': //init { if (CmdCompare("/initGame")) { char subtxt[6] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); currentGame.BWind = CheckWind(subtxt); SubContxt(subtxt, command_contxt, 0, ' '); currentGame.SWind = CheckWind(subtxt); currentGame.OpenAt = command_contxt[0] - '0'; currentGame.Master = command_contxt[2] - '0'; currentGame.Mcombo = command_contxt[4] - '0'; } // 發牌(input) else if (CmdCompare("/initCard")) { char subtxt[4] = {0}; //if莊家 17張手牌, else 16張手牌 if(currentGame.PlayerID == currentGame.Master) hand_size = 17; else hand_size = 16; for(short i = 0; i < hand_size; i++) { SubContxt(subtxt, command_contxt, 0, ' '); short tile_ini = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); hand[i] = tile_ini; table[tile_ini] = currentGame.PlayerID; } } break; } // 摸(input) case 'm': //mo { short tile_mo = (command_contxt[0] - '0') * 100 + (command_contxt[1] - '0') * 10 + (command_contxt[2] - '0'); table[tile_mo] = currentGame.PlayerID; hand[hand_size++] = tile_mo; break; } // 詢問AI動作(input) case '.': //ask /. t { // throw丟(output) if (command_contxt[0] == 't') { if (LastThrow != 0 && GetType(LastThrow) != 4) // 上回合沒有吃碰槓(數牌處理) { short P = currentGame.PlayerID; for(int i = 0; i < 3; i++) { if (P != currentGame.PlayerID) HeatProcess(LastThrow, P); P = (P % 4) +1; } Allsum[RemoveID(LastThrow)]--; } // 判定手牌狀況 bool numeric = false, symbol = false; // numeric=有數字牌, symbol=有字牌 // 優先丟字牌 閥值 = 2 short minm4 = 5, minSymbol = 0; cout << "check symbol\n"; for (short i = 0; i < hand_size; i++) { if (GetType(hand[i]) == 4) { if (minm4 > Allsum[RemoveID(hand[i])]) { minSymbol = i; minm4 = Allsum[RemoveID(hand[i])]; cout << hand[i] << " " << Allsum[RemoveID(hand[i])] << endl; } symbol = true; } else numeric = true; } if (minm4 <= 2) // 閥值 = 2 numeric = false; cout << "Result: " << symbol << " " << numeric << " " << minm4 << endl; if (numeric && minm4 > 2) { bool filtered = false; short iter = 0; short filtered_size = 0; short heatsum[23] = {0}; short tile_filtered[23] = {0}; short cold = 0; for (short i = 0; i < hand_size; i++) if (GetType(hand[i]) != 4) tile_filtered[iter++] = hand[i]; filtered_size = iter; // 半分法 for (short i = 0; i < filtered_size; i++) for (short j = 1; j <= 4; j++) if (j != currentGame.PlayerID) heatsum[i] += heat[RemoveID(tile_filtered[i])][j]; for (short half = initial_heat37 * 3 / 2; half > 0; half /= 2) { iter = 0; filtered = true; for (short i = 0; i < filtered_size; i++) if (heatsum[i] < half) { heatsum[iter] = heatsum[i]; tile_filtered[iter++] = tile_filtered[i]; filtered = false; } if (filtered) break; // if沒有低於half的牌, 就跳出 for (short i = iter; i < filtered_size; i++) tile_filtered[i] = 0, heatsum[i] = 0; filtered_size = iter; } if (filtered_size > 1) { // 篩選熱度值差異 filtered = true; iter = 0; for(short i = 0; i < filtered_size; i++) { short maxm = 0; for (short j = 0; j < 4; j++) if (j != currentGame.PlayerID) if (maxm < heat[RemoveID(tile_filtered[i])][j]) maxm = heat[RemoveID(tile_filtered[i])][j]; if (heatsum[i] >= 2*maxm) { heatsum[iter] = heatsum[i]; tile_filtered[iter++] = tile_filtered[i]; filtered = false; } } if (!filtered) { for (short i = iter; i < filtered_size; i++) tile_filtered[i] = 0, heatsum[i] = 0; filtered_size = iter; } if (filtered_size) { // 計算SumOrder short SumOrder[3] = {0}; short Sum[5]; for (short p = 1, j = 0; p <= 4; p++) if (p != currentGame.PlayerID) { SumOrder[j++] = p; for (short i = 11; i <= 39; i++) if (i%10 != 0) // 跳過20 30 Sum[p] += heat[i][p]; } if (Sum[SumOrder[0]] > Sum[SumOrder[1]]) { short t = SumOrder[0]; SumOrder[0] = SumOrder[1]; SumOrder[1] = t; } if (Sum[SumOrder[0]] > Sum[SumOrder[2]]) { short t = SumOrder[0]; SumOrder[0] = SumOrder[2]; SumOrder[2] = t; } if (Sum[SumOrder[1]] > Sum[SumOrder[2]]) { short t = SumOrder[1]; SumOrder[1] = SumOrder[2]; SumOrder[2] = t; } cout << "三家優先度: " << SumOrder[0] << " " << SumOrder[1] << " " << SumOrder[2] << endl; // 剩餘張數 short minm = 5; for(short i = 0; i < filtered_size; i++) { if (minm > Allsum[RemoveID(tile_filtered[i])]) { minm = Allsum[RemoveID(tile_filtered[i])]; cold = i; } else if (minm == Allsum[RemoveID(tile_filtered[i])]) // 針對順位之熱度值排序 { short order = 0; if(heat[RemoveID(tile_filtered[cold])][SumOrder[order]] == heat[RemoveID(tile_filtered[i])][SumOrder[order]]) { if (heat[RemoveID(tile_filtered[cold])][SumOrder[++order]] == heat[RemoveID(tile_filtered[i])][SumOrder[order]]) { if (heat[RemoveID(tile_filtered[cold])][SumOrder[++order]] > heat[RemoveID(tile_filtered[i])][SumOrder[order]]) cold = i; } else if (heat[RemoveID(tile_filtered[cold])][SumOrder[order]] > heat[RemoveID(tile_filtered[i])][SumOrder[order]]) cold = i; } else if (heat[RemoveID(tile_filtered[cold])][SumOrder[order]] > heat[RemoveID(tile_filtered[i])][SumOrder[order]]) cold = i; } } } } if (!symbol || Allsum[RemoveID(tile_filtered[cold])] < minm4) //丟數字 { cout << "/throw " << tile_filtered[cold] << endl; } else //丟字牌 { cout << "/throw " << hand[minSymbol] << endl; } } else //丟字牌 { cout << "/throw " << hand[minSymbol] << endl; } } // eat吃(output) else if (command_contxt[0] == 'e') { cout << "/pass" << endl; } // pong碰(output) else if (command_contxt[0] == 'p') { cout << "/pass" << endl; } // gong槓(output) else if (command_contxt[0] == 'g') { cout << "/pass" << endl; /* short gong_type = command_contxt[2] - '0'; if (gong_type == 4) // 明槓 { // collect info } else if (gong_type == 0) // 暗槓 { // collect info } else if (gong_type == 1) // 加槓 { // collect info } */ } // hu胡(output) else if (command_contxt[0] == 'h') { cout << "/hu" << endl; } break; } //他家棄牌(input) case 't': //throw { short playerID = command_contxt[0] - '0'; short tile_id = (command_contxt[2] - '0') * 100 + (command_contxt[3] - '0') * 10 + (command_contxt[4] - '0'); if (playerID == currentGame.PlayerID) { short temp; for (temp = 0; temp < hand_size; temp++) if (hand[temp] == tile_id) break; table[hand[temp]] = currentGame.PlayerID * -1; hand[temp] = hand[hand_size-1]; hand[--hand_size] = 0; } if (LastThrow != 0) // 上回合沒有吃碰槓(數牌處理) { if (GetType(LastThrow) != 4) { short P = playerID; HeatProcess(LastThrow, P); for(short i = 0; i < 4; i++) { if (P != currentGame.PlayerID) HeatProcess(LastThrow, P); P = (P % 4) +1; } } Allsum[RemoveID(LastThrow)]--; } LastThrow = tile_id; if (GetType(tile_id) != 4) HeatProcess(tile_id, playerID); table[tile_id] = playerID * -1; break; } // 他家 吃、碰 case 'e': case 'p': { char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short playerID = subtxt[0] - '0'; if (playerID == currentGame.PlayerID) { table[LastThrow] = currentGame.PlayerID; hand[hand_size++] = LastThrow; } short tile_id; for (short i = 0; i < 3; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile_id = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); table[tile_id] = playerID; if (GetType(tile_id) != 4) for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) HeatProcess(tile_id, p); Allsum[RemoveID(tile_id)]--; } LastThrow = 0; break; } // 他家 槓 case 'g': { char subtxt[3] = {0}; short tile_id[4]; SubContxt(subtxt, command_contxt, 0, ' '); short playerID = subtxt[0] - '0'; SubContxt(subtxt, command_contxt, 0, ' '); short Gtype = subtxt[0] - '0'; if (Gtype == 0) break; //暗槓則跳過 if (playerID == currentGame.PlayerID && Gtype == 4) { table[LastThrow] = currentGame.PlayerID; hand[hand_size++] = LastThrow; } SubContxt(subtxt, command_contxt, 0, ' '); tile_id[0] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); if (Gtype != 1) // 明槓 for (short i = 1; i < 4; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile_id[i] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); table[tile_id[i]] = playerID; if (GetType(tile_id[0]) != 4) //非字牌 for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) HeatProcess(tile_id[i], p); } else // 加槓 { table[tile_id[0]] = playerID; if (GetType(tile_id[0]) != 4) for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) HeatProcess(tile_id[0], p); } Allsum[RemoveID(tile_id[0])] = 0; LastThrow = 0; break; } // 確認AI有連接(?) case 'r': //ready cout << "/ready" << endl; break; // 程式結束(input) case 'q': //quit return 0; // Other(input) default: if (CmdCompare("*/print")) { for (short i = 11; i <= 47; i++) { if (i%10 != 0) { change_output(i * 10); cout << " "; cout << "剩張: " << Allsum[i] << "\t"; if (i < 40) for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) { cout << ' ' << p << ':'; cout.width(4); cout << heat[i][p]; } } cout << endl; } } else if (CmdCompare("*/check")) { char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short tile = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); cout << "剩張: " << Allsum[RemoveID(tile)] << "\t"; if (GetType(tile) != 4) { cout << "Heat: "; for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) if (p != currentGame.PlayerID) { cout << ' ' << p << ':'; cout.width(4); cout << heat[RemoveID(tile)][p]; } cout << "\t"; } cout << endl; } else if (CmdCompare("*/acheck")) { for (short i = 0; i < hand_size; i++) { change_output(hand[i]); cout << " "; cout << "剩張: " << Allsum[RemoveID(hand[i])] << "\t"; if (GetType(hand[i]) != 4) { cout << "Heat: "; for (short p = 1; p <= 4; p++) if (p != currentGame.PlayerID) if (p != currentGame.PlayerID) { cout << ' ' << p << ':'; cout.width(4); cout << heat[RemoveID(hand[i])][p]; } cout << "\t"; } cout << endl; } } else cout << "Unknown Command!" << endl; break; } //end of switch } //end of while true return -1; } bool CmdCompare(const char* CmdToCmp) { bool flag = true; short i; for (i = 0; CmdToCmp[i] != 0; i++) { if(command_title[i] != CmdToCmp[i] || i > 20 || command_title[i] == 0) { flag = false; break; } } return flag; } bool StrCompare(char* context, const char* Compare) //compare maximum 20 characters { bool flag = true; short i; for (i = 0; Compare[i] != 0; i++) if(context[i] != Compare[i] || i > 20 || context[i] == 0) { flag = false; break; } return flag; } void SubContxt(char* subArr, char* mainArr, short begin, char target) { short i; for (i = begin; *(mainArr + i) != target && *(mainArr + i) != 0; i++) { //cout << *(mainArr + i); *(subArr + i) = *(mainArr + i); } for (short j = begin; *(mainArr + j) != 0; j++) { *(mainArr + j) = *(mainArr + j + i + 1); } } short CheckWind(char* wind) { switch (*wind) { case 'E': return 1; case 'S': return 2; case 'W': return 3; case 'N': return 4; default: return -1; } } void GameReset(void) { hand_size = 0; for (short i = 0; i < 23; i++) hand[i] = 0; for (short i = 11; i <= 47; i++) { if (i%10 != 0) // 跳過20 30 40 { table[i*10 + 0] = 0; table[i*10 + 1] = 0; table[i*10 + 2] = 0; table[i*10 + 3] = 0; Allsum[i] = 4; } } for (short i = 11; i <= 39; i++) { if (i%10 == 0) // 跳過20 30 40 continue; else if (inRange(3, i % 10, 7)) // 3 ~ 7 { heat[i][1] = initial_heat37; heat[i][2] = initial_heat37; heat[i][3] = initial_heat37; heat[i][4] = initial_heat37; } else if (inRange(2, i % 10, 8)) // 2 或 8 { heat[i][1] = initial_heat28; heat[i][2] = initial_heat28; heat[i][3] = initial_heat28; heat[i][4] = initial_heat28; } else // 1 或 9 { heat[i][1] = initial_heat19; heat[i][2] = initial_heat19; heat[i][3] = initial_heat19; heat[i][4] = initial_heat19; } } } void HeatProcess(short tile_id, short playerID) { if (GetType(tile_id) != 4) { short m2, m1, z0, p1, p2; m2 = RemoveID(tile_id) - 2; m1 = RemoveID(tile_id) - 1; z0 = RemoveID(tile_id); p1 = RemoveID(tile_id) + 1; p2 = RemoveID(tile_id) + 2; if (inRange(3, GetDigit(tile_id), 7)) { heat[m2][playerID] -= Allsum[m2]*Allsum[m1]; heat[m1][playerID] -= Allsum[m2]*Allsum[m1] + Allsum[m1]*Allsum[p1]; heat[z0][playerID] -= Allsum[m2]*Allsum[m1] + Allsum[m1]*Allsum[p1] + Allsum[p1]*Allsum[p2]; heat[p1][playerID] -= Allsum[m1]*Allsum[p1] + Allsum[p1]*Allsum[p2]; heat[p2][playerID] -= Allsum[p1]*Allsum[p2]; } else if (GetDigit(tile_id) == 2) // 2 { heat[m1][playerID] -= Allsum[m1]*Allsum[p1]; heat[z0][playerID] -= Allsum[m1]*Allsum[p1] + Allsum[p1]*Allsum[p2]; heat[p1][playerID] -= Allsum[m1]*Allsum[p1] + Allsum[p1]*Allsum[p2]; heat[p2][playerID] -= Allsum[p1]*Allsum[p2]; } else if (GetDigit(tile_id) == 8) // 8 { heat[m2][playerID] -= Allsum[m2]*Allsum[m1]; heat[m1][playerID] -= Allsum[m1]*Allsum[p1] + Allsum[m2]*Allsum[m1]; heat[z0][playerID] -= Allsum[m1]*Allsum[p1] + Allsum[m2]*Allsum[m1]; heat[p1][playerID] -= Allsum[m1]*Allsum[p1]; } else if (GetDigit(tile_id) == 1)// 1 { heat[z0][playerID] -= Allsum[p1]*Allsum[p2]; heat[p1][playerID] -= Allsum[p1]*Allsum[p2]; heat[p2][playerID] -= Allsum[p1]*Allsum[p2]; } else if (GetDigit(tile_id) == 9) // 9 { heat[z0][playerID] -= Allsum[m2]*Allsum[m1]; heat[m1][playerID] -= Allsum[m2]*Allsum[m1]; heat[m2][playerID] -= Allsum[m2]*Allsum[m1]; } } else cout << "Heat Error!" << endl; } short GetType(short tile_id) { // 如果title_id > 999, print warning if(tile_id > 999) cout << "[Warning] - The tile_id > 999.\n"; // return type return tile_id / 100; } short GetDigit(short tile_id) { // 如果title_id > 999, print warning if(tile_id > 999) cout << "[Warning] - The tile_id > 999.\n"; // return digit return (tile_id / 10) % 10; } short GetID(short tile_id) { // 如果title_id > 999, print warning if(tile_id > 999) cout << "[Warning] - The tile_id > 999.\n"; // return id return tile_id % 10; } short RemoveID(short tile_id) { return tile_id / 10; } bool inRange(short a, short x ,short b) { if (a <= x && x <= b) return true; return false; } void change_output(short title_id) { if(GetType(title_id) == 1) cout << GetDigit(title_id) << "w "; else if(GetType(title_id) == 2) cout << GetDigit(title_id) << "t "; else if(GetType(title_id) == 3) cout << GetDigit(title_id) << "s "; else if(GetDigit(title_id) == 1) cout << "東"; else if(GetDigit(title_id) == 2) cout << "南"; else if(GetDigit(title_id) == 3) cout << "西"; else if(GetDigit(title_id) == 4) cout << "北"; else if(GetDigit(title_id) == 5) cout << "中"; else if(GetDigit(title_id) == 6) cout << "發"; else cout << "白"; }