---
# System prepended metadata

title: NewGuard 3/27

---

# NewGuard 3/27
```
#include <iostream>
#include <string.h>
using namespace std;
#define initial_heat37 192
#define initial_heat28 128
#define initial_heat19 64

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);
void MuscleProcess(short, short);
short MuscleHeat(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);

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 Restsum[48] = {4}; // 記錄所有牌種剩餘數量(還有幾張未被棄出)
short Drawtime[48][5]; // 手牌變更度紀錄
short Heat[40]; // 記錄數牌可能性
short muscle[40][5]; // 紀錄筋牌影響來源;

short LastThrow = 0; // 最後一個棄牌
short LastPlayer = 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 'a': //ask
        {
            //cout << "test ask\n";

            // throw丟(output)
            if (command_contxt[0] == 't')
            {
                if (LastThrow != 0) // 上回合沒有吃碰槓(時間處理)
                {
                    for (short p = 1; p <= 4; p++)
                        if (p != currentGame.PlayerID && p != LastPlayer)
                            Drawtime[RemoveID(LastThrow)][p] = 0;
                }

                short subhand[23] = {0};
                short subhand_size = Hand_size;
                for(short i = 0; i < Hand_size; i++)
                    subhand[i] = Hand[i];

                bool throwEnd = false, numeric = false;
                short tothrow = 0; //the tile to throw

                // time check & replicant count
                short reps[48] = {0}; //replicant count
                for (short i = 0; i < subhand_size; i++)
                {
                    reps[RemoveID(subhand[i])]++;

                    short t_sum = 0;
                    for (short p = 1; p <= 4; p++)
                        if (p != currentGame.PlayerID)
                        {
                            if (Drawtime[RemoveID(subhand[i])][p] != -1)
                                t_sum += Drawtime[RemoveID(subhand[i])][p];
                            else
                            {
                                t_sum = -1;
                                break;
                            }
                        }
                    if (t_sum == 0)
                    {
                        if (!throwEnd)
                        {
                            tothrow = subhand[i];
                            throwEnd = true;
                //cout << "time check throw" << endl;
                        }
                        else if ( (GetType(subhand[i]) != 4 && GetType(tothrow) == 4) || // 數牌 > 字牌
                                    (Restsum[RemoveID(subhand[i])] > Restsum[RemoveID(tothrow)]) ) // 剩張多 > 剩張少
                        {
                            tothrow = subhand[i];
                        }
                    }
                }

                // total sum (4 - restsum + replicants) of symbol = 4
                short minsum = 6;
                if (!throwEnd)
                {
                    for (short i = 0; i < subhand_size; i++)
                    {
                        if (GetType(subhand[i]) == 4)
                        {
                            short s = Restsum[RemoveID(subhand[i])] - reps[RemoveID(subhand[i])];
           // cout << "s: " << s << endl;
                            if (s == 0)
                            {
                                tothrow = subhand[i];
                                throwEnd = true;
               //cout << "total sum throw" << endl;
                                break;
                            }
                            else if (s < minsum)
                            {
                                minsum = s;
                                tothrow = subhand[i];
                            }
                        }
                        else numeric = true;
                    }
                }
            //cout << "current throw: " << tothrow << endl;

                // numeric calculation
                if (!throwEnd && numeric)
                {
                    short endHeat = 0, minHeat = initial_heat37*3 + 1;
                    short htothrow = 0;
                    for (short i = 0; i < subhand_size; i++)
                    {
                        if (GetType(subhand[i]) == 4) continue;
            //cout << "(num)current tile: " << subhand[i];
                        endHeat = 0;
                        for (short p = 1; p <= 4; p++)
                            if (p != currentGame.PlayerID)
                                endHeat += (Drawtime[subhand[i]][p] == 0 && muscle[RemoveID(subhand[i])][p] != 0)? //只有最後一張棄牌才納入筋牌計算
                                            MuscleHeat(subhand[i], p) : Heat[RemoveID(subhand[i])];
            //cout << "  endHeat = " << endHeat << " v " << minHeat << endl;

                        // +time calc

                        if (endHeat < minHeat)
                        {
                            minHeat = endHeat;
                            htothrow = subhand[i];
                        }
                    }
            //cout << "htothrow: " << htothrow << endl;
                    if (minsum > Restsum[RemoveID(htothrow)] +1) tothrow = htothrow;
                }
                else throwEnd = true;

                cout << "/throw " << tothrow << endl << flush;

                if (GetType(tothrow) != 4)
                    HeatProcess(tothrow);
                Restsum[RemoveID(tothrow)]--;
                LastThrow = tothrow;

                short temp;
                for (temp = 0; temp < Hand_size; temp++)
                    if (Hand[temp] == tothrow)
                        break;
                if(temp != Hand_size)
                {
                    Table[Hand[temp]] = currentGame.PlayerID * -1;
                    Hand[temp] = Hand[Hand_size-1];
                    Hand[--Hand_size] = 0;
                }
            }

            // 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 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)
            {
                if(Table[tile_id] == currentGame.PlayerID)
                {
                    if (LastThrow != 0) // 上回合沒有吃碰槓(時間處理)
                    {
                        for (short p = 1; p <= 4; p++)
                            if (p != currentGame.PlayerID && p != LastPlayer)
                                Drawtime[RemoveID(LastThrow)][p] = 0;
                    }

                    Restsum[RemoveID(tile_id)]--;

                    short temp;
                    for (temp = 0; temp < Hand_size; temp++)
                        if (Hand[temp] == tile_id)
                            break;
                    if(temp != Hand_size)
                    {
                        Table[Hand[temp]] = currentGame.PlayerID * -1;
                        Hand[temp] = Hand[Hand_size-1];
                        Hand[--Hand_size] = 0;
                    }
                }
            }
            else
            {
                if (LastThrow != 0) // 上回合沒有吃碰槓(時間處理)
                {
                    for (short p = 1; p <= 4; p++)
                        if (p != currentGame.PlayerID && p != LastPlayer)
                            Drawtime[RemoveID(LastThrow)][p] = 0;

                    // drawtime++
                    for (short i = 11; i <= 47; i++)
                        if (Drawtime[i][playerID] != -1)
                            Drawtime[i][playerID]++;
                }

                Drawtime[RemoveID(tile_id)][playerID] = 0; //棄牌家時間歸零
                LastThrow = tile_id;
                if (GetType(tile_id) != 4)
                {
                    HeatProcess(tile_id);
                    MuscleProcess(tile_id, playerID);
                }
                Table[tile_id] = playerID * -1;

                Restsum[RemoveID(tile_id)]--;
            }

            LastPlayer = playerID;

            break;
        }

        // 吃
        case 'e':
        {
            char subtxt[3] = {0};
            SubContxt(subtxt, command_contxt, 0, ' ');

            short playerID = subtxt[0] - '0';
            short tile_id;
            if (playerID == currentGame.PlayerID)
                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');

                    short temp;
                    for (temp = 0; temp < Hand_size; temp++)
                        if (Hand[temp] == tile_id)
                            break;
                    if(temp != Hand_size)
                    {
                        Table[Hand[temp]] = currentGame.PlayerID * -1;
                        Hand[temp] = Hand[Hand_size-1];
                        Hand[--Hand_size] = 0;
                    }
                }

            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');
                if(tile_id == LastThrow) continue;

                Table[tile_id] = playerID;

                if (GetType(tile_id) != 4)
                    HeatProcess(tile_id);

                Restsum[RemoveID(tile_id)]--;
            }

            // drawtime 0
            for(short p = 1; p <= 4; p++)
            {
                if (p != currentGame.PlayerID && p != LastPlayer)
                    Drawtime[RemoveID(LastThrow)][p] = 0;
            }

            LastThrow = 0;
            break;
        }

        // 碰
        case 'p':
        {
            char subtxt[3] = {0};
            SubContxt(subtxt, command_contxt, 0, ' ');

            short playerID = subtxt[0] - '0';
            short tile_id;
            if (playerID == currentGame.PlayerID)
                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');

                    short temp;
                    for (temp = 0; temp < Hand_size; temp++)
                        if (Hand[temp] == tile_id)
                            break;
                    if(temp != Hand_size)
                    {
                        Table[Hand[temp]] = currentGame.PlayerID * -1;
                        Hand[temp] = Hand[Hand_size-1];
                        Hand[--Hand_size] = 0;
                    }
                }

            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');
                if(tile_id == LastThrow) continue;

                Table[tile_id] = playerID;

                if (GetType(tile_id) != 4)
                    HeatProcess(tile_id);

                Restsum[RemoveID(tile_id)]--;
            }

            LastThrow = 0;
            break;
        }

        // 槓
        case 'g':
        {
            char subtxt[3] = {0};
            SubContxt(subtxt, command_contxt, 0, ' ');
            short playerID = subtxt[0] - '0';
            SubContxt(subtxt, command_contxt, 0, ' ');
            short Gtype = subtxt[0] - '0';
            short tile_id;
            if (playerID == currentGame.PlayerID)
            {
                    for (short i = 0; i < (Gtype != 1)? 4 : 1; i++)
                    {
                        SubContxt(subtxt, command_contxt, 0, ' ');
                        tile_id = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');

                        short temp;
                        for (temp = 0; temp < Hand_size; temp++)
                            if (Hand[temp] == tile_id)
                                break;
                        if(temp != Hand_size)
                        {
                            Table[Hand[temp]] = currentGame.PlayerID * -1;
                            Hand[temp] = Hand[Hand_size-1];
                            Hand[--Hand_size] = 0;
                        }
                    }
            }


            // drawtime++
            if(playerID != currentGame.PlayerID)
                for (short i = 11; i <= 47; i++)
                    if (Drawtime[i][playerID] != -1)
                        Drawtime[i][playerID]++;

            if (Gtype == 0) break; //若為暗槓則跳過

            if (Gtype != 1) // 明槓
                for (short i = 0; i < 4; i++)
                {
                    SubContxt(subtxt, command_contxt, 0, ' ');
                    tile_id = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
                    if(tile_id == LastThrow) continue;

                    Table[tile_id] = playerID;

                    if (GetType(tile_id) != 4)
                        HeatProcess(tile_id);
                }
            else // 加槓
            {
                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)
                    HeatProcess(tile_id);
            }
            Restsum[RemoveID(tile_id)] = 0;

            LastThrow = 0;
            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"))
            {
            }
            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;
            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][1] = 0;
        muscle[i][2] = 0;
        muscle[i][3] = 0;
        muscle[i][4] = 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_id)
{
    /*if (GetType(tile_id) == 4)
        cout << "Heat Error!" << endl;*/

    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] -= 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_id) == 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_id) == 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_id) == 1)// 1
    {
        Heat[z0] -= Restsum[p1]*Restsum[p2];
        Heat[p1] -= Restsum[p1]*Restsum[p2];
        Heat[p2] -= Restsum[p1]*Restsum[p2];
    }
    else if (GetDigit(tile_id) == 9) // 9
    {
        Heat[z0] -= Restsum[m2]*Restsum[m1];
        Heat[m1] -= Restsum[m2]*Restsum[m1];
        Heat[m2] -= Restsum[m2]*Restsum[m1];
    }
}

void MuscleProcess(short tile_id, short playerID)
{
    short m3 = RemoveID(tile_id) - 3;
    short p3 = RemoveID(tile_id) + 3;

    if (inRange(1, m3, 9))
        muscle[m3][playerID] = GetDigit(tile_id);
    if (inRange(1, p3, 9))
        muscle[p3][playerID] = GetDigit(tile_id);
}

short MuscleHeat(short tile_id, short playerID)
{
    /*if (GetType(tile_id) == 4)
        cout << "MHeat Error!" << endl;*/

    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;
    short mheat = -1;
    //cout << muscle[z0][playerID] << " from " << tile_id << " " << playerID << endl;
    switch (muscle[z0][playerID])
    {
    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_id) == 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_id) == 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_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;
}