---
# System prepended metadata

title: Shell.cpp 3/7

---

# Shell.cpp 3/7
```
#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);

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[500] = {0}; // 記錄所有牌的狀況 <tile_id , status> (-x = player x 的棄牌, 0 = 未知, x = player x 的手牌)
short Allsum[50] = {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 'a': //ask
            {
                //cout << "test ask\n";

                // throw丟(output)
                if (command_contxt[0] == 't')
                {
                    if (LastThrow != 0 && GetType(LastThrow) != 4) // 上回合沒有吃碰槓(數牌處理)
                    {
                //cout << "Last throw\n";
                        short P = (currentGame.PlayerID % 4) +1;
                        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++)
                    {
                //cout << "checking.....\n";
                        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)
                    {
                //cout << "into if\n";
                        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;

                        
                        // 半分法
                        
                //cout << "Halfing\n";
                        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 << "Last\n";
                                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 << flush;
                            Allsum[RemoveID(tile_filtered[cold])]--;
                            LastThrow = tile_filtered[cold];

                            short temp;
                            for (temp = 0; temp < hand_size; temp++)
                                if (hand[temp] == tile_filtered[cold])
                                    break;
                            table[hand[temp]] = currentGame.PlayerID * -1;
                            hand[temp] = hand[hand_size-1];
                            hand[--hand_size] = 0;
                        }
                        else //丟字牌
                        {
                            cout << "/throw " << hand[minSymbol] << endl << flush;
                            Allsum[RemoveID(hand[minSymbol])]--;
                            LastThrow = hand[minSymbol];

                            table[hand[minSymbol]] = currentGame.PlayerID * -1;
                            hand[minSymbol] = hand[hand_size-1];
                            hand[--hand_size] = 0;
                        }
                    }
                    else //丟字牌
                    {
                        cout << "/throw " << hand[minSymbol] << endl << flush;
                        Allsum[RemoveID(hand[minSymbol])]--;
                        LastThrow = hand[minSymbol];

                        table[hand[minSymbol]] = currentGame.PlayerID * -1;
                        hand[minSymbol] = 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;
                    
                    /*
                    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';
                if (playerID == currentGame.PlayerID) break;

                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)]--;
                }

                short tile_id = (command_contxt[2] - '0') * 100 + (command_contxt[3] - '0') * 10 + (command_contxt[4] - '0');
                LastThrow = tile_id;
                if (GetType(tile_id) != 4)
                    HeatProcess(LastThrow, 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) break;

                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[3];

                SubContxt(subtxt, command_contxt, 0, ' ');
                short playerID = subtxt[0] - '0';
                if (playerID == currentGame.PlayerID) break;

                SubContxt(subtxt, command_contxt, 0, ' ');
                short Gtype = subtxt[0] - '0';
                if (Gtype == 0) break; //若為暗槓則跳過
                
                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)
                        {
                        cout << i;
                        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
                    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;
}
