# Guard_f.cpp ###### tags: `Current Ver.` ``` #include <iostream> #include <cstdlib> #include <string.h> #include "Guard.h" using namespace std; #define initial_heat37 192 #define initial_heat28 128 #define initial_heat19 64 struct Gameinfo{ short playerSelf = 1, BWind = 1, SWind = 1, OpenAt = 1, Master = 1, MCombo = 0; void SetSelfID(short player_ID){ playerSelf = player_ID; } void SetInfo(short BW, short SW, short OP, short GM, short MC){ BWind = BW; SWind = BW; OpenAt = OP; Master = GM; MCombo = MC; } Gameinfo() {} }; Gameinfo currentGame; short Hand[17]; //Hand can contain upmost to 17 tiles short Hand_size; //Hand's size short Table[474]; //all tile's current status <tile , status> (-x = player x's thrown tile, 0 = unknown, x = player x's tile) short Restsum[48]; //tile's non-thrown count short Drawtime[48][5]; //players Hand change overtime short Heat[40]; //numeric tile's possiblity to form a straight short muscle[40]; //muscle affect source short tothrow; short LastThrow = 0; // 最後一個棄牌 short LastPlayer = 0; // 上一個玩家 char command_title[20]; // 指令 char command_contxt[70]; // 指令參數 short Guard(void) { // 開始(input) switch (command_title[1]) //first letter from command { case 's': //start currentGame.SetSelfID((short)command_contxt[3] - '0'); break; // 本局資訊(input) case 'i': //init { if (CmdCompare("/initGame")) { char BWind[6] = {0}; char SWind[6] = {0}; SubContxt(BWind, command_contxt, 0, ' '); SubContxt(SWind, command_contxt, 0, ' '); short OpenAt, Master, MCombo; OpenAt = (short)command_contxt[0] - '0'; Master = (short)command_contxt[2] - '0'; MCombo = (short)command_contxt[4] - '0'; currentGame.SetInfo(CheckWind(BWind), CheckWind(SWind), OpenAt, Master, MCombo); //set game info GameReset(); //reset variables } // 發牌(input) else if (CmdCompare("/initCard")) { char subtxt[4] = {0}; short tile_ini[17] = {0}; //if莊家 17張手牌, else 16張手牌 if(currentGame.playerSelf == currentGame.Master) Hand_size = 17; else Hand_size = 16; for(short i = 0; i < Hand_size; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile_ini[i] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); } SetHand(tile_ini); //set hand tiles } break; } // 摸(input) case 'm': //mo { short tile_mo = (command_contxt[0] - '0') * 100 + (command_contxt[1] - '0') * 10 + (command_contxt[2] - '0'); addHand(tile_mo); //add tile to hand break; } // 詢問AI動作(input) case 'a': //ask { // throw丟(output) if (command_contxt[0] == 't') { Guard_AskThrow(); } // 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; } // hu胡(output) else if (command_contxt[0] == 'h') { cout << "/hu" << endl; } break; } //棄牌(input) case 't': //throw { short player_ID = command_contxt[0] - '0'; short tile = (command_contxt[2] - '0') * 100 + (command_contxt[3] - '0') * 10 + (command_contxt[4] - '0'); Guard_Throw(player_ID, tile); break; } // 吃 case 'e': { /*char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; short tile; for (short i = 0; i < 3; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); if (player_ID == currentGame.playerSelf) { RemoveHand(tile); } if(tile == LastThrow) continue; Table[tile] = player_ID; if (GetType(tile) != 4) HeatProcess(tile); Restsum[RemoveID(tile)]--; } // drawtime 0 DrawTimeZero(LastThrow, 0); LastThrow = 0; break;*/ char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; short tile[3]; for (short i = 0; i < 3; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile[i] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); } Guard_Eat(player_ID, tile); break; } // 碰 case 'p': { /*char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; short tile; for (short i = 0; i < 3; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); if (player_ID == currentGame.playerSelf) RemoveHand(tile); if(tile == LastThrow) continue; Table[tile] = player_ID; if (GetType(tile) != 4) HeatProcess(tile); Restsum[RemoveID(tile)]--; } LastThrow = 0; break;*/ char subtxt[3] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; short tile[3] = {0}; for (short i = 0; i < 3; i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile[i] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); if(tile[i] == LastThrow) continue; } Guard_Pong(player_ID, tile); break; } // 槓 case 'g': { /*char subtxt[3] = {0}; short tile; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; SubContxt(subtxt, command_contxt, 0, ' '); short Gtype = subtxt[0] - '0'; // drawtime+ DrawTimePlus(player_ID); for (short i = 0; i < ((Gtype != 1)? 4 : 1); i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); if (player_ID == currentGame.playerSelf) RemoveHand(tile); if (tile == LastThrow || Gtype == 0) continue;//若為暗槓則跳過 Table[tile] = player_ID; if (GetType(tile) != 4) HeatProcess(tile); } LastThrow = 0; break;*/ char subtxt[3] = {0}; short tile_arr[4] = {0}; SubContxt(subtxt, command_contxt, 0, ' '); short player_ID = subtxt[0] - '0'; SubContxt(subtxt, command_contxt, 0, ' '); short Gtype = subtxt[0] - '0'; for (short i = 0; i < ((Gtype != 1)? 4 : 1); i++) { SubContxt(subtxt, command_contxt, 0, ' '); tile_arr[i] = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0'); } Guard_Gong(player_ID, tile_arr, Gtype); break; } // 確認AI有連接 case 'r': //ready cout << "/ready" << endl; break; // 有人胡牌 case 'h': //hu break; // 程式結束(input) case 'q': //quit return 0; // Other(input) default: if (CmdCompare("*/print")) { cout << endl << "Hand size = " << Hand_size << endl; for (short i = 0; i < Hand_size; i++) { change_output(Hand[i]); cout << " "; cout << "剩張: " << Restsum[RemoveID(Hand[i])] << "\t"; if (GetType(Hand[i]) != 4) { cout << "Heat: "; cout.width(4); cout << Heat[RemoveID(Hand[i])]; } cout << endl; } } else if (CmdCompare("*/time")) { cout << endl << "Hand size = " << Hand_size << endl; for (short i = 0; i < Hand_size; i++) { change_output(Hand[i]); cout << " time: "; for (short p = 1; p <= 4; p++) cout << Drawtime[RemoveID(Hand[i])][p] << " "; cout << endl; } } else cout << "Unknown Command!" << endl; break; } //end of switch 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; Restsum[i] = 4; Drawtime[i][1] = -1; Drawtime[i][2] = -1; Drawtime[i][3] = -1; Drawtime[i][4] = -1; } } for (short i = 11; i <= 39; i++) { muscle[i] = 0; if (i%10 == 0) // 跳過20 30 40 continue; else if (inRange(3, i % 10, 7)) // 3 ~ 7 { Heat[i] = initial_heat37; } else if (inRange(2, i % 10, 8)) // 2 或 8 { Heat[i] = initial_heat28; } else // 1 或 9 { Heat[i] = initial_heat19; } } } void HeatProcess(short tile) { if (GetType(tile) == 4) return; short m2, m1, z0, p1, p2; m2 = RemoveID(tile) - 2; m1 = RemoveID(tile) - 1; z0 = RemoveID(tile); p1 = RemoveID(tile) + 1; p2 = RemoveID(tile) + 2; if (inRange(3, GetDigit(tile), 7)) { Heat[m2] -= Restsum[m2]*Restsum[m1]; Heat[m1] -= Restsum[m2]*Restsum[m1] + Restsum[m1]*Restsum[p1]; Heat[z0] -= Restsum[m2]*Restsum[m1] + Restsum[m1]*Restsum[p1] + Restsum[p1]*Restsum[p2]; Heat[p1] -= Restsum[m1]*Restsum[p1] + Restsum[p1]*Restsum[p2]; Heat[p2] -= Restsum[p1]*Restsum[p2]; } else if (GetDigit(tile) == 2) // 2 { Heat[m1] -= Restsum[m1]*Restsum[p1]; Heat[z0] -= Restsum[m1]*Restsum[p1] + Restsum[p1]*Restsum[p2]; Heat[p1] -= Restsum[m1]*Restsum[p1] + Restsum[p1]*Restsum[p2]; Heat[p2] -= Restsum[p1]*Restsum[p2]; } else if (GetDigit(tile) == 8) // 8 { Heat[m2] -= Restsum[m2]*Restsum[m1]; Heat[m1] -= Restsum[m1]*Restsum[p1] + Restsum[m2]*Restsum[m1]; Heat[z0] -= Restsum[m1]*Restsum[p1] + Restsum[m2]*Restsum[m1]; Heat[p1] -= Restsum[m1]*Restsum[p1]; } else if (GetDigit(tile) == 1)// 1 { Heat[z0] -= Restsum[p1]*Restsum[p2]; Heat[p1] -= Restsum[p1]*Restsum[p2]; Heat[p2] -= Restsum[p1]*Restsum[p2]; } else if (GetDigit(tile) == 9) // 9 { Heat[z0] -= Restsum[m2]*Restsum[m1]; Heat[m1] -= Restsum[m2]*Restsum[m1]; Heat[m2] -= Restsum[m2]*Restsum[m1]; } } void MuscleProcess(short tile, short player_ID) { if (GetType(tile) == 4) return; short m3 = RemoveID(tile) - 3; short p3 = RemoveID(tile) + 3; if (inRange(1, GetDigit(m3), 9)) muscle[m3] = tile; if (inRange(1, GetDigit(p3), 9)) muscle[p3] = tile; // cout << m3 << " muscle: " << muscle[m3] << endl; // cout << p3 << " muscle: " << muscle[p3] << endl; } short MuscleHeat(short tile, short player_ID) { if (GetType(tile) == 4) return 0; short m2, m1, z0, p1, p2; m2 = RemoveID(tile) - 2; m1 = RemoveID(tile) - 1; z0 = RemoveID(tile); p1 = RemoveID(tile) + 1; p2 = RemoveID(tile) + 2; short mheat = 0; //cout << muscle[z0][player_ID] << " from " << tile << " " << player_ID << endl; switch (GetDigit(muscle[z0])) { case 1: case 2: case 3: mheat += Restsum[m1]*Restsum[z0]*Restsum[p1]; mheat += Restsum[z0]*Restsum[p1]*Restsum[p2]; break; case 7: case 8: case 9: mheat += Restsum[m2]*Restsum[m1]*Restsum[z0]; mheat += Restsum[m1]*Restsum[z0]*Restsum[p1]; break; case 4: if (GetDigit(tile) == 1) mheat = 0; else { mheat += Restsum[m1]*Restsum[z0]*Restsum[p1]; mheat += Restsum[z0]*Restsum[p1]*Restsum[p2]; } break; case 5: mheat = Restsum[m1]*Restsum[z0]*Restsum[p1]; break; case 6: if (GetDigit(tile) == 9) mheat = 0; else { mheat += Restsum[m2]*Restsum[m1]*Restsum[z0]; mheat += Restsum[m1]*Restsum[z0]*Restsum[p1]; } break; default: //cout << "MHeat Error!" << endl; break; } return mheat; } short GetType(short tile) { // 如果title_id > 999, print warning /*if(tile > 999) cout << "[Warning] - The tile > 999.\n";*/ // return type return tile / 100; } short GetDigit(short tile) { // 如果title_id > 999, print warning /*if(tile > 999) cout << "[Warning] - The tile > 999.\n";*/ // return digit return (tile / 10) % 10; } short GetID(short tile) { // 如果title_id > 999, print warning /*if(tile > 999) cout << "[Warning] - The tile > 999.\n";*/ // return id return tile % 10; } short RemoveID(short tile) { return tile / 10; } bool inRange(short a, short x ,short b) { if (a <= x && x <= b) return true; return false; } void arrClear(short *Arraytoclear, short Arraysize, short Clearvalue = 0) { for (short i = 0; i < Arraysize; i++) Arraytoclear[i] = Clearvalue; } 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 << "白"; } void InputMethod() { char inputchar; bool is_title = true; for (short title_size = 0, contxt_size = 0; title_size < 20 && contxt_size < 70;) { inputchar = cin.get(); 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; } } /*************************************************************************/ void Guard_AskThrow() { // cout << "LastThrow = " << LastThrow << endl; if (LastThrow != 0) // 上回合沒有吃碰槓(時間處理) DrawTimeZero(LastThrow, 0); bool throwEnd = false, numeric = false; tothrow = 0; //the tile to throw short reps[48] = {0}; //replicant count short t_max[23]; arrClear(t_max, short(23), short(-1)); short t_sum[23]; arrClear(t_sum, short(23), short(-1)); //time check throwEnd = TimeCheck(t_max, t_sum); //replicant count for (short i = 0; i < Hand_size; i++) reps[RemoveID(Hand[i])]++; // cout << "current throw1: " << tothrow << endl; //check for min unknown sum //total sum (4 - restsum + replicants) of symbol = 4 short minsum = 6; if (!throwEnd) { throwEnd = SumCheck(&minsum, reps); } // cout << "current throw2: " << tothrow << endl; // numeric calculation if (!throwEnd) { short heatthrow = HeatCalculation(t_sum); if (minsum > Restsum[RemoveID(heatthrow)]) tothrow = heatthrow; // cout << minsum << " vs " << Restsum[RemoveID(heatthrow)] << endl; } else throwEnd = true; cout << "/throw " << tothrow << endl << flush; } void Guard_Throw(short player_ID, short tile) { if (player_ID == currentGame.playerSelf) { RemoveHand(tile); // change_output(tile); // cout << Restsum[RemoveID(tile)] << endl; } if (LastThrow != 0) // 上回合沒有吃碰槓(時間處理) { DrawTimeZero(LastThrow, 0); // drawtime+ DrawTimePlus(player_ID); } DrawTimeZero(tile, player_ID); //棄牌家時間歸零 LastThrow = tile; if (GetType(tile) != 4) { HeatProcess(tile); MuscleProcess(tile, player_ID); } Table[tile] = player_ID * -1; Restsum[RemoveID(tile)]--; LastPlayer = player_ID; } void Guard_Eat(short player_ID, short *tile) { for (short i = 0; i < 3; i++) { if(tile[i] == LastThrow) continue; if (player_ID == currentGame.playerSelf) { RemoveHand(tile[i]); } Table[tile[i]] = player_ID; if (GetType(tile[i]) != 4) HeatProcess(tile[i]); Restsum[RemoveID(tile[i])]--; } // drawtime 0 DrawTimeZero(LastThrow, 0); LastThrow = 0; } void Guard_Pong(short player_ID, short *tile) { for (short i = 0; i < 3; i++) { if (player_ID == currentGame.playerSelf) RemoveHand(tile[i]); if(tile[i] == LastThrow) continue; Table[tile[i]] = player_ID; if (GetType(tile[i]) != 4) HeatProcess(tile[i]); Restsum[RemoveID(tile[i])]--; } LastThrow = 0; } void Guard_Gong(short player_ID, short *tile, short Gtype) { // drawtime+ DrawTimePlus(player_ID); for (short i = 0; i < ((Gtype != 1)? 4 : 1); i++) { if (player_ID == currentGame.playerSelf) RemoveHand(tile[i]); if (tile[i] == LastThrow || Gtype == 0) continue;//若為暗槓則跳過 Table[tile[i]] = player_ID; if (GetType(tile[i]) != 4) HeatProcess(tile[i]); } LastThrow = 0; } /*************************************************************************/ void SetHand(const short *tile_arr) { //if莊家 17張手牌, else 16張手牌 if(currentGame.playerSelf == currentGame.Master) Hand_size = 17; else Hand_size = 16; for(short i = 0; i < Hand_size; i++) { Hand[i] = tile_arr[i]; Table[tile_arr[i]] = currentGame.playerSelf; } } void addHand(short tile) { Table[tile] = currentGame.playerSelf; Hand[Hand_size++] = tile; } void RemoveHand(short tile) { short temp; for (temp = 0; temp < Hand_size; temp++) if (Hand[temp] == tile) break; if(temp != Hand_size) { Table[Hand[temp]] = currentGame.playerSelf * -1; Hand[temp] = Hand[Hand_size-1]; Hand[--Hand_size] = 0; //cout << "Removed " << tile << " from Hand, Hand size = " << Hand_size << endl; } } void DrawTimePlus(short player_ID) { for (short i = 11; i <= 47; i++) if (Drawtime[i][player_ID] != -1) Drawtime[i][player_ID]++; } void DrawTimeZero(short tile, short player_ID) //player_ID = 0 for all player_ID except self { if (player_ID == 0) for (short p = 1; p <= 4; p++) if (p != currentGame.playerSelf) Drawtime[RemoveID(tile)][p] = 0; else Drawtime[RemoveID(LastThrow)][player_ID] = 0; } bool TimeCheck(short *t_max, short *t_sum) { bool throwEnd = false; for (short i = 0; i < Hand_size; i++) { for (short p = 1; p <= 4; p++) if (p != currentGame.playerSelf) { if (Drawtime[RemoveID(Hand[i])][p] > t_max[i]) t_max[i] == Drawtime[RemoveID(Hand[i])][p]; if (Drawtime[RemoveID(Hand[i])][p] > -1) t_sum[i] += Drawtime[RemoveID(Hand[i])][p]; } if (t_sum[i] != -1) t_sum[i]++; if (t_max[i] == 0) //follow throw, surely safe { if (!throwEnd) { tothrow = Hand[i]; throwEnd = true; // cout << "time check throw" << endl; } else if ( (GetType(Hand[i]) != 4 && GetType(tothrow) == 4) || // 數牌 > 字牌 (Restsum[RemoveID(Hand[i])] > Restsum[RemoveID(tothrow)]) ) // 剩張多 > 剩張少 { tothrow = Hand[i]; } } } return throwEnd; } bool SumCheck(short *minsum_ptr, const short *reps) { for (short i = 0; i < Hand_size; i++) { if (GetType(Hand[i]) == 4) { short s = Restsum[RemoveID(Hand[i])] - reps[RemoveID(Hand[i])]; // cout << "s: " << s << endl; if (s == 0) { tothrow = Hand[i]; *minsum_ptr = s; //cout << "total sum throw" << endl; return true; } else if (s < *minsum_ptr) { *minsum_ptr = s; tothrow = Hand[i]; } } } return false; } short HeatCalculation(const short *t_sum) { short endHeat = 0, minHeat = initial_heat37*3 + 1; short htothrow = 0; for (short i = 0; i < Hand_size; i++) { if (GetType(Hand[i]) == 4) continue; //no heat for char tile //cout << "(num)current tile: " << Hand[i]; endHeat = 0; for (short p = 1; p <= 4; p++) if (p != currentGame.playerSelf) { endHeat += /*(muscle[RemoveID(Hand[i])] != 0 && Drawtime[RemoveID(muscle[Hand[i]])][p] == 0)?*/ (GetType(LastThrow) == GetType(Hand[i]) && abs(GetDigit(LastThrow)-GetDigit(Hand[i])) == 3 && muscle[RemoveID(Hand[i])] != 0)? MuscleHeat(Hand[i], p) : Heat[RemoveID(Hand[i])]; // , cout << (GetType(LastThrow) == GetType(Hand[i])) << " " << // (abs(GetDigit(LastThrow)-GetDigit(Hand[i])) == 3) << " " << // muscle[RemoveID(Hand[i])] << endl; } //time calc // if (t_max[i] != -1 && t_max[i] <= 3) // endHeat = endHeat * t_max[i] / 3; if (t_sum[i] != -1 && t_sum[i] <= 9) endHeat = endHeat * t_sum[i] / 9; // cout << Hand[i] << " endHeat = " << endHeat << " v " << minHeat << endl; if (endHeat < minHeat) { minHeat = endHeat; htothrow = Hand[i]; } // else numeric = true; } // cout << "htothrow: " << htothrow << endl; return htothrow; }