# Shell.cpp 2/17
```
#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 GetTimes(short title_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));
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
{
// throw丟(output)
if (command_contxt[0] == 't')
{
// 判定手牌狀況
bool numeric = false, symbol = false; // numeric有數字牌, symbol有字牌
// 優先丟字牌 閥值 = 2
short minm4 = 5, minSymbol = 0;
for (short i = 0; i < hand_size; i++)
{
if (GetType(hand[i]) == 4)
{
if (Allsum[hand[i]] <= 2) // 閥值 = 2
if (minm4 > Allsum[hand[i]])
minm4 = Allsum[hand[i]], minSymbol = i;
symbol = true;
}
else
numeric = true;
}
if (numeric && minm4 > 2)
{
bool filtered = false;
short iter = 0;
short filtered_size = hand_size;
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[i] = hand[i];
// 半分法
for (short half = initial_heat37 * 3 / 2; half > 0; half /= 2)
{
iter = 0;
filtered = true;
for (short i = 0; i < filtered_size; i++)
for (short j = 0; j < 4; j++)
if (j != currentGame.PlayerID)
heatsum[i] += heat[RemoveID(tile_filtered[i])][j];
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 i = 1, j = 0; i <= 4; i++)
if (i != currentGame.PlayerID)
{
SumOrder[j++] = i;
for (short j = 11; j < 39; j++)
if (j%10 != 0) // 跳過20 30
Sum[i] += heat[j][i];
}
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;
}
// 剩餘張數
short minm = 5;
for(short i = 0; i < filtered_size; i++)
{
if (minm > Allsum[tile_filtered[i]])
{
minm = Allsum[tile_filtered[i]];
cold = i;
}
else if (minm == Allsum[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[tile_filtered[cold]] < minm4)
{
cout << "/throw " << 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];
table[hand[minSymbol]] = currentGame.PlayerID * -1;
hand[minSymbol] = hand[hand_size-1];
hand[--hand_size] = 0;
}
}
else
{
cout << "/throw " << 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')
{
// collect info
}
// pong碰(output)
else if (command_contxt[0] == 'p')
{
// collect info
}
// gong槓(output)
else if (command_contxt[0] == 'g')
{
short playerID = command_contxt[0] - '0';
if (playerID == currentGame.PlayerID) break;
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";
}
break;
}
//他家棄牌(input)
case 't': //throw
{
short playerID = command_contxt[0] - '0';
if (playerID == currentGame.PlayerID) break;
if (LastThrow != 0) // 上回合沒有人吃碰槓
{
Allsum[RemoveID(LastThrow)]++;
short P = playerID - 1;
HeatProcess(LastThrow, P);
for(int i = 0; i < 3; i++)
{
HeatProcess(LastThrow, P);
P = (P+1) % 4;
}
Allsum[RemoveID(LastThrow)]--;
}
short tile_id = (command_contxt[2] - '0') * 100 + (command_contxt[3] - '0') * 10 + (command_contxt[4] - '0');
LastThrow = tile_id;
table[tile_id] = playerID * -1;
HeatProcess(tile_id, playerID);
HeatProcess(tile_id, playerID);
Allsum[RemoveID(tile_id)]--;
break;
}
// 他家 吃、碰
case 'e':
case 'p':
{
Allsum[GetType(LastThrow)*10 + GetDigit(LastThrow)]++;
LastThrow = 0;
short playerID = command_contxt[0] - '0';
if (playerID == currentGame.PlayerID) break;
char subtxt[3] = {0};
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id1 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id2 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id3 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
table[tile_id1] = playerID;
table[tile_id2] = playerID;
table[tile_id3] = playerID;
Allsum[RemoveID(tile_id1)]--;
Allsum[RemoveID(tile_id2)]--;
Allsum[RemoveID(tile_id3)]--;
if (GetType(tile_id1) != 4)
for (short i = 0; i < 4; i++)
{
HeatProcess(tile_id1, playerID);
HeatProcess(tile_id2, playerID);
HeatProcess(tile_id3, playerID);
}
break;
}
// 他家 槓
case 'g':
{
LastThrow = 0;
short playerID = command_contxt[0] - '0';
if (playerID == currentGame.PlayerID) break;
char subtxt[3] = {0};
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id1 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id2 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id3 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
SubContxt(subtxt, command_contxt, 0, ' ');
short tile_id4 = (subtxt[0] - '0') * 100 + (subtxt[1] - '0') * 10 + (subtxt[2] - '0');
table[tile_id1] = playerID;
table[tile_id2] = playerID;
table[tile_id3] = playerID;
table[tile_id4] = playerID;
Allsum[RemoveID(tile_id1)] = 0;
if (GetType(tile_id1) != 4)
for (short i = 0; i < 4; i++)
{
HeatProcess(tile_id1, playerID);
HeatProcess(tile_id2, playerID);
HeatProcess(tile_id3, playerID);
HeatProcess(tile_id4, playerID);
}
break;
}
// 確認AI有連接(?)
case 'r': //ready
break;
// 程式結束(input)
case 'q': //quit
return 0;
// Error(input)
default:
cout << "Unknown Command!" << endl;
break;
}
} //end of while true
return -1;
}
bool CmdCompare(const char* CmdToCmp)
{
bool flag = true;
for (short i = 0; command_title[i] != 0; i++)
if(command_title[i] != CmdToCmp[i] || i > 10)
{
flag = false;
break;
}
return flag;
}
bool StrCompare(char* context, const char* Compare) //compare maximum 20 characters
{
bool flag = true;
for (short i = 0; context[i] != 0; i++)
if(context[i] != Compare[i] || i > 20)
{
flag = false;
break;
}
return flag;
}
void SubContxt(char* subArr, char* mainArr, short begin, char target)
{
short i;
for (i = begin; *(mainArr + i - 1) != target && *(mainArr + i - 1) != 0; i++)
{
// cout << *(mainArr + i);
*(subArr + i) = *(mainArr + i);
}
for (short j = begin; *(mainArr + j) != 0; j++)
{
if (*(mainArr + j + i) != 0)
*(mainArr + j) = *(mainArr + j + i);
else
*(mainArr + j) = 0;
}
}
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][0] = initial_heat37;
heat[i][1] = initial_heat37;
heat[i][2] = initial_heat37;
heat[i][3] = initial_heat37;
}
else if (inRange(2, i % 10, 8)) // 2 或 8
{
heat[i][0] = initial_heat28;
heat[i][1] = initial_heat28;
heat[i][2] = initial_heat28;
heat[i][3] = initial_heat28;
}
else // 1 或 9
{
heat[i][0] = initial_heat19;
heat[i][1] = initial_heat19;
heat[i][2] = initial_heat19;
heat[i][3] = 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;
}