#include #include #include #include #include //棋譜の記録に使う const int HACMNUM = 29; const int SASTUNIT = 100; //千日手用ハッシュ配列の長さ単位 const char* NAMES[] = {"リディア", "オヴィ", "クリス", "ギル", "フルミネア", "リュウ", "メル", "ラルドゥラ", "ザナ", "パール", "ミルフ", "ファーヴァ", "ルージュ", "セレン", "ラヴァ", "ウムトナ", "リーネ", "レレゾナ", "ジール", "リナ", "エケトネ", "エンナ", "アッシュ", "ネーネ", "ピネナ", "ンムト", "クノン", "クミール", "テームス"}; //各駒の初期状態。位置はアルシェ側を下にして左上から右に向かって数える //0-48はアルシェ側、49はアルシェ側未テームス、50-98はアルシェ側方向転換、99はアルシェ側現テームス、 //100-148はソーン側未テームス、149はソーン側未テームス、150-198はソーン側方向転換、199はソーン側現テームス //200-248はテームス、249はアルシェ側既テームス、250-298はテームス方向転換、299はソーン側既テームス //駒じゃなくて盤面に状態を記すほうが良かったか? const int INITSTAT[] = {46, 47, 43, 40, 36, 42, 44, 48, 39, 35, 37, 38, 41, 45, 100, 113, 105, 108, 112, 107, 104, 106, 111, 101, 102, 110, 109, 103, 224}; //駒の種類。0:横、1:縦、2:斜、3:塔、4:跳、5:色、6:扉、7:王、8:テームス const int HACMKIND[] = {6, 4, 4, 0, 0, 5, 6, 5, 2, 1, 2, 3, 1, 7, 5, 1, 4, 0, 0, 1, 6, 5, 2, 4, 6, 3, 2, 7, 8}; const char* KINDNAMES[] = {"横", "縦", "斜", "塔", "跳", "色", "扉", "王", "テ"}; const char* SOOMNAMES[] = {"velm", "erva", "satii", "teeve", "beezel", "ilva", "part"}; const char* MODES[] = {"self", "easy", "normal", "hard", "lunatic"}; void PrintStat(int hacmstat[], int sastnum, int sorn); void PrintName(int hacmid); int ExecCommand(const char* c, int hacmstat[], int sorn, int vp); void WriteRecord(char* c, int vp, char* filename); int Score(int hacmstat[]); bool CheckRepetition(int hacmstat[], int sastnum); bool CheckS(int hacmstat[], int m, int pl); int Check(int hacmstat[], int pl); bool Checkmate(int hacmstat[], int pl, int check); void EasyMode(char *c, int hacmstat[], int pl, int vp); int main(void) { int i, flag; int vp; //視点(画面下)となる側。0ならアルシェ、1ならソーン int sastnum; //手番の数 int player; //現在のターンのプレイヤー int check; //チェック状態 int mode; //プレイモード int hacmstat[HACMNUM]; int tempstat[HACMNUM]; char* recadd = "record.txt"; char c[50], d[50]; char as[50]; while(1){ printf("プレイモードもしくは読み込む局の開始日時を入力してください:"); fgets(as, 50, stdin); as[strlen(as) - 1] = '\0'; //改行文字を終端記号に変換 //初期化 for( i = 0; i < HACMNUM; ++i ) hacmstat[i] = INITSTAT[i]; sastnum = 0; player = 0; //モード mode = -1; for( i = 0; i < 5; ++i ) { if(strcmp(as, MODES[i]) == 0) { mode = i; break; } } //プレイ if(mode != -1) { printf("arxe側(先手)かsorn側(後手)か選んでください(arxe:0、sorn:1):"); while(1){ fgets(as, 50, stdin); if(strcmp(as, "0\n") == 0) { vp = 0; break; } else if (strcmp(as, "1\n") == 0) { vp = 1; break; } } int score = -1; int repflag = 0; //千日手フラグ //開始状態記録 char sorxe[50]; sprintf(sorxe, "%s : %s", (vp == 0) ? "arxe" : "sorn", MODES[mode]); WriteRecord(sorxe, vp, recadd); //ゲームメインループ(1ターンで1周) do { PrintStat(hacmstat, sastnum, vp); if(score > -1) break; //詰み判定。詰んでいたらループを出る check = Check(hacmstat, player); if(check > 0) { if(Checkmate(hacmstat, player, check)) { printf("%s", (check == 1) ? "月駒" : "魔駒"); printf("チェックメイトです\n"); if(check == 1) { score = 9 - 5 * player; } //魔駒で詰んでいる場合、最後の一手で相手の魔駒を取れるなら取って終了とする else { score = 5 - 5 * player; flag = 0; for(i = 14 - 14 * player; i < 27 - 14 * player; ++i) { if(HACMKIND[i] > 4 && hacmstat[i] % 50 < 49) { ++score; if(CheckS(hacmstat, hacmstat[i] % 50, 1 - player)) flag = 1; } } score -= flag; } break; } else { printf("%s", (check == 1) ? "月駒" : (check == 2) ? "魔駒" : "月駒・魔駒"); printf("チェックです\n"); } } //千日手判定。同一局面2回目で警告、次の手でもう一回同一局面に入るとアウト //これで尽くせてるかどうかは不明 if(CheckRepetition(hacmstat, sastnum)) { if(repflag == 0) { printf("警告:次の手で前回と同じ手を指すと千日手と判定されます\n"); repflag = 1; } else break; } else { repflag = 0; } if(mode == 0 || player == vp) { printf("コマンドを入力してください:"); fgets(c, 50, stdin); c[strlen(c)-1] = '\0'; //ゼロコマンドで前回の入力キャンセル if(sastnum > 0) { if(strcmp(c, "") == 0) { if(mode == 0) { --sastnum; player = sastnum % 2; } else { sastnum -= 2; } for(i = 0; i < HACMNUM; ++i) hacmstat[i] = tempstat[i]; PrintStat(hacmstat, sastnum, vp); printf("前回の入力を取り消しました\n"); printf("コマンドを入力してください:"); fgets(c, 50, stdin); c[strlen(c)-1] = '\0'; } else { WriteRecord(d, vp, recadd); if(mode > 0) WriteRecord(as, vp, recadd); } } for(i = 0; i < HACMNUM; ++i) tempstat[i] = hacmstat[i]; //有効なコマンドが入力されるまでループ while(ExecCommand(c, hacmstat, player, vp) < 1){ printf("\nコマンドを入力してください:"); fgets(c, 50, stdin); c[strlen(c)-1] = '\0'; } //入力キャンセルの場合に備えて今回の入力を保存しておく strcpy(d, c); } //ここCPUモードとか入れるスペース else if(mode == 1) { EasyMode(as, hacmstat, player, player); ExecCommand(as, hacmstat, player, 0); } ++sastnum; player = sastnum % 2; score = Score(hacmstat); } while(1); WriteRecord(d, vp, recadd); if(score > 5) { printf("得点%dで%sの勝利です\n", score - 5, "後手"); sprintf(c, "%s tafat %d ito var %d sast", "sorn", score - 5, sastnum); } else if(score > -1) { printf("得点%dで%sの勝利です\n", score, "先手"); sprintf(c, "%s tafat %d ito var %d sast", "arxe", score, sastnum); } else { printf("千日手で%sの負けです\n", (player == 1) ? "先手": "後手"); sprintf(c, "%s vadet man daks im sast %d", (player == 1) ? "arxe" : "sorn", sastnum); } WriteRecord(c, vp, recadd); } //棋譜リプレイ else { FILE *fp; char s[256]; if ((fp = fopen(recadd, "r")) == NULL) { printf("棋譜ファイル'%s'を読み込めません\n", recadd); continue; } //"kit:"で始まる行を検索 while(fgets(s, 256, fp) != NULL) { s[strlen(s)-1] = '\0'; if(strstr(s, "kit:") != NULL && strstr(s, as) != NULL) { printf("棋譜の読み込みを開始します\n%s\n", s); break; } } if(fgets(s, 256, fp) != NULL) { if(strstr(s, "sorn")) vp = 1; else vp = 0; //読み込みメインループ while(strstr(s, "took") == NULL) { fgets(s, 256, fp); s[strlen(s)-1] = '\0'; PrintStat(hacmstat, sastnum, vp); ExecCommand(s, hacmstat, sastnum % 2, 0); fgets(c, 50, stdin); ++sastnum; } printf("%s", s); fgets(s, 256, fp); printf("%s棋譜の読み込みを終了しました\n", s); } fclose(fp); } } return 0; } //特定の場所の状態を判定する //返り値が0-28ならその駒によって占められている。-1は空所 int PlaceStat(int hacmstat[], int place) { int i; for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] % 50 == place ) return i; } return -1; } //テームスを除く特定の駒が特定の場所に動けるか否か。 //idは駒の番号、hstatは駒の状態、pは駒の現在位置、mは駒の移動先、mstatは移動先の状態 bool CanMoveS(int hacmstat[], int id, int hstat, int p, int m, int mstat) { int i; int kind = HACMKIND[id]; //横駒、縦駒(方向転換) if( (kind == 0 && hstat % 100 < 49) || (kind == 1 && hstat % 100 > 49) ) { if( (p == m-1 && p%7 < 6) || (p == m+1 && p%7 > 0) ) return true; else return false; } //縦駒、横駒(方向転換) if( (kind == 1 && hstat % 100 < 49) || (kind == 0 && hstat % 100 > 49) ) { if( p == m-7 || p == m+7 ) return true; else return false; } //斜駒 if( kind == 2 && hstat % 100 < 49 ) { if( (p == m-8 && p%7 < 6) || (p == m+8 && p%7 > 0) || (p == m-6 && p%7 > 0) || (p == m+6 && p%7 < 6) ) return true; else return false; } //斜駒(方向転換) if( kind == 2 && hstat % 100 > 49 ) { if( p == m-7 || p == m+7 || (p == m-1 && p%7 < 6) || (p == m+1 && p%7 > 0) ) return true; else return false; } //塔駒 if( kind == 3 && hstat % 100 < 49 ) { if( p == m-7 || p == m+7 || (p == m-14 && PlaceStat(hacmstat, m-7) == -1) || //飛び越え不可 (p == m+14 && PlaceStat(hacmstat, m+7) == -1) ) return true; else return false; } //塔駒(方向転換) if( kind == 3 && hstat % 100 > 49) { if( (p == m-1 && p%7 < 6) || (p == m+1 && p%7 > 0) || (p == m-2 && p%7 < 5 && PlaceStat(hacmstat, m-1) == -1) || (p == m+2 && p%7 > 1 && PlaceStat(hacmstat, m+1) == -1) ) return true; else return false; } //跳駒 if( kind == 4 ) { if( (p == m-15 && p%7 < 6) || (p == m+15 && p%7 > 0) ||(p == m-13 && p%7 > 0) || (p == m+13 && p%7 < 6) ||(p == m-9 && p%7 < 5) || (p == m+9 && p%7 > 1) ||(p == m-5 && p%7 > 1) || (p == m+5 && p%7 < 5)) return true; else return false; } //色駒 if( kind == 5 ) { //左方向 for( i = 1; i <= p % 7; ++i ) { if( p == m+i ) return true; //途中に駒がいたらそれ以上進めない if( PlaceStat(hacmstat, m+i) > -1 ) break; } //上方向 for( i = 1; i <= p / 7; ++i ) { if( p == m + 7*i ) return true; if( PlaceStat(hacmstat, m + 7*i) > -1 ) break; } //右方向 for( i = 1; i <= 6 - p % 7; ++i ) { if( p == m-i ) return true; if( PlaceStat(hacmstat, m-i) > -1 ) break; } //下方向 for( i = 1; i <= (48 - p) / 7; ++i ) { if( p == m - 7*i ) return true; if( PlaceStat(hacmstat, m - 7*i) > -1 ) break; } return false; } //扉駒 if( kind == 6 ) { //右下、左上、左下、右下の順に処理 for( i = 1; i <= (48 - p) / 8; ++i ) { if( p == m - 8*i && p%7 < 7-i ) return true; if( PlaceStat(hacmstat, m - 8*i) > -1 ) break; } for( i = 1; i <= p / 8; ++i ) { if( p == m + 8*i && p%7 > i-1 ) return true; if( PlaceStat(hacmstat, m + 8*i) > -1 ) break; } for( i = 1; i <= (48 - p) / 6; ++i ) { if( p == m - 6*i && p%7 > i-1 ) return true; if( PlaceStat(hacmstat, m - 6*i) > -1 ) break; } for( i = 1; i <= p / 6; ++i ) { if( p == m + 6*i && p%7 < 7-i ) return true; if( PlaceStat(hacmstat, m + 6*i) > -1 ) break; } //隣に駒がいないとき軸移動できる if( mstat == -1 && ( (p == m-1 && p%7 < 6) || (p == m+1 && p%7 > 0) || p == m-7 || p == m+7 ) ) return true; else return false; } //月駒 if( kind == 7 ) { if( ( (p == m-8 || p == m+6 || p == m-1) && p%7 < 6) || ( (p == m+8 || p == m-6 || p == m+1) && p%7 > 0) || p == m-7 || p == m+7 ) return true; else return false; } return false; } //テームスを含む特定の駒が特定の場所に移動できるか否か //hacmidは移動する駒の番号、mは移動先、sornは移動主体がアルシェなら0でソーンなら1、zuihanはそれが随伴移動か否か bool CanMove(int hacmstat[], int hacmid, int m, int sorn, bool zuihan) { int hstat = hacmstat[hacmid]; int p = hstat % 50; int mstat = PlaceStat(hacmstat, m); //移動先の状態 int id = hacmid; int i, flag; //その場所に味方がいる場合、移動できない if( mstat > -1 && mstat / 14 == sorn ) return false; //移動先がテームスで、かつ相手がテームスに何も張っていない場合、移動できない if( mstat == 28 ) { flag = 0; for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] == 99 + 100 * (1 - sorn) ) { flag = 1; break; } } if(flag == 0) return false; } //その場所に敵がいる場合、随伴移動できない if( mstat > -1 && zuihan ) return false; //前衛以外は随伴移動できない if( HACMKIND[id] > 3 && zuihan ) return false; //テームスを張られている駒と擬似的に見なす if( id == 28 ) { for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] == 99 + 100 * sorn ) id = i; } } return CanMoveS(hacmstat, id, hstat, p, m, mstat); } //試合が終了しているかどうか調べる。 //0-4ならアルシェ勝利、5-9ならソーン勝利、-1は未決着 //上の数字の幅は得点。8ならソーンが3点獲得して勝利 int Score(int hacmstat[]) { //魔駒がなくなる場合と月駒が追い込まれる場合とに分けて判定 int i, sorn; int art[] = {0, 0}; //魔駒残数 for ( sorn = 0; sorn < 2; ++sorn ) { for( i = 14 * sorn; i < 14 + 14 * sorn; ++i ) { if( HACMKIND[i] == 7 && hacmstat[i] % 50 == 49 ) return 4 + 5 * sorn; else if( HACMKIND[i] == 5 && hacmstat[i] % 50 < 49 ) ++art[sorn]; else if( HACMKIND[i] == 6 && hacmstat[i] % 50 < 49 ) ++art[sorn]; } } for ( sorn = 0; sorn < 2; ++sorn ) { //相手の魔駒がゼロなら勝利 if( art[1-sorn] == 0 ) return art[sorn] + 5 * sorn; } return -1; } //----------詰み判定関係-----------// //特定の場所mに敵駒が利いているかどうかを判定する bool CheckS(int hacmstat[], int m, int pl) { int i; int mstat = PlaceStat(hacmstat, m); for(i = 14 - 14 * pl; i < 28 - 14 * pl; ++i) { if(CanMoveS(hacmstat, i, hacmstat[i], hacmstat[i] % 50, m, mstat)) { return true; } } //テームス if(CanMove(hacmstat, 28, m, 1 - pl, false)) return true; return false; } //プレイヤーplがチェックされているかどうかを調べる //チェックとは月駒ないし残り1つの魔駒に敵駒が利いている状態を指すとする //返り値は1なら月駒チェック、2なら魔駒チェック、3なら両方チェック、0ならノーチェック int Check(int hacmstat[], int pl) { int i; int result = 0; int count = 0; //月駒 if(CheckS(hacmstat, hacmstat[pl*14+13] % 50, pl)) result = 1; //魔駒。魔駒が既に0個のときはチェックされていることになってしまうがまぁいいか。 for(i = 14 * pl; i < 13 + 14 * pl; ++i) { if(HACMKIND[i] < 5) continue; if(!CheckS(hacmstat, hacmstat[i] % 50, pl)) return result; else ++count; } //残り1つの魔駒がチェックされている場合のみ魔駒チェック if(count == 1) return result + 2; else return result; } //駒idがpに存在する駒へのp+dに存在する敵駒による利きを合駒で防げるか否か //iは敵の駒の移動のインターバル bool CanObstructS(int hacmstat[], int id, int p, int d, int pl, int i) { if(d % i == 0) { int j; if(d > 0) { for(j = 1; j < d / i; ++j) { if(CanMove(hacmstat, id, p + i * j, pl, false)) return true; } } else if(d < 0) { for(j = -1; j > d / i; --j) { if(CanMove(hacmstat, id, p + i * j, pl, false)) return true; } } } return false; } //プレイヤーplが特定の場所pへの敵駒iの利きを合駒で防げるか否か bool CanObstruct(int hacmstat[], int p, int i, int pl) { int j, id, a, b; int m = hacmstat[i] % 50; if(i == 28){ for( j = 0; j < HACMNUM; ++j ) { if( hacmstat[j] == 199 - 100 * pl ) i = j; } } for(id = 14 * pl; id < 14 + 14 * pl; ++id) { //敵が塔駒のとき if(HACMKIND[i] == 3) { //敵と月駒が2マス以上離れていて、間に移動可能ならば合駒可能 if((m - p) % 2 == 0 && CanMove(hacmstat, id, p + (m - p) / 2, false)) return true; } //敵が色駒のとき else if(HACMKIND[i] == 5) { //縦に並んでいる場合 if(CanObstructS(hacmstat, id, p, m - p, pl, 7)) return true; //横に並んでいる場合 else if(CanObstructS(hacmstat, id, p, m - p, pl, 1)) return true; } //敵が扉駒のとき else if(HACMKIND[i] == 6) { //右斜めに並んでいる場合 if(CanObstructS(hacmstat, id, p, m - p, pl, 8)) return true; //左斜めに並んでいる場合 else if(CanObstructS(hacmstat, id, p, m - p, pl, 6)) return true; } } return false; } //プレイヤーplがチェックメイトされているかどうかを調べる //checkはCheck関数の返り値。0のときは適用できない bool Checkmate(int hacmstat[], int pl, int check) { int i, j, flag, id; //移動による解消(月駒) if(check == 1) { id = pl * 14 + 13; int p = hacmstat[id] % 50; int mo[] = {-8, -7, -6, -1, 1, 6, 7, 8}; for(i = 0; i < 8; ++i) { //移動可能でかつ敵の駒が利いていない場所があればチェックメイトでない if(CanMove(hacmstat, id, p + mo[i], pl, false) && !CheckS(hacmstat, p + mo[i], pl)) return false; } } //移動による解消(魔駒) else if(check == 2) { for(id = 14 * pl; id < 13 + 14 * pl; ++id) { if(HACMKIND[id] < 5) continue; //全マスについてCanMoveを適用しているので非効率 for(i = 0; i < 49; ++i) { if(CanMove(hacmstat, id, i, pl, false) && CheckS(hacmstat, i, pl)) return false; } } } //すべての敵駒について、チェックに関わっているかどうかを調べる for(i = 0; i < HACMNUM; ++i) { if(i != 28 && (14 - 14 * pl > i || i > 28 - 14 * pl)) continue; //合駒による解消 //月駒へのチェック if(check != 2 && CanMove(hacmstat, i, hacmstat[pl * 14 + 13] % 50, 1 - pl, false)) { if(CanObstruct(hacmstat, hacmstat[pl * 14 + 13] % 50, i, pl)) return false; } //魔駒へのチェック else if(check != 1) { for(id = 14 * pl; id < 13 + 14 * pl; ++id) { if(HACMKIND[id] < 5) continue; if(CanMove(hacmstat, i, hacmstat[id] % 50, 1 - pl, false) && CanObstruct(hacmstat, hacmstat[id] % 50, i, pl)) return false; else break; } } else continue; //攻撃による解消 int a, b; for(id = 14 * pl; id < 14 + 14 * pl; ++id) { //チェックしている駒を味方の駒で取れるとき、 //実際に取ってみてチェックが解消されるならばチェックメイトでない if(CanMove(hacmstat, id, hacmstat[i] % 50, pl, false)) { a = hacmstat[id]; b = hacmstat[i]; hacmstat[id] += hacmstat[i] % 50 - hacmstat[id] % 50; hacmstat[i] = 149 - pl * 100; if(!Check(hacmstat, pl)) { hacmstat[id] = a; hacmstat[i] = b; return false; } hacmstat[id] = a; hacmstat[i] = b; } } } return true; } //---------千日手関係---------// unsigned int* hashes; unsigned int Hash(int hacmstat[]) { int i; unsigned int h = 0; for(i = 0; i < HACMNUM; ++i) { //2^0+2^7+2^15 h = h * 32897 + hacmstat[i]; } //2^31-1周期 return h % 2147483647; } bool CheckRepetition(int hacmstat[], int sastnum) { int i; if(sastnum == 0) hashes = malloc(SASTUNIT * sizeof(int)); //SASTUNITごとにハッシュ配列を延長 else if(sastnum % SASTUNIT == 0) realloc(hashes, (sastnum/SASTUNIT + 1) * SASTUNIT); hashes[sastnum] = Hash(hacmstat); for(i = 0; i < sastnum; ++i) { if(hashes[i] == hashes[sastnum]) return true; } return false; } //x座標とy座標の数から位置を計算 //x,yは1-7の数字で指定 int Place(int x, int y, int vp) { if(vp == 0) return x + 7 * y - 8; else return 56 - x - 7 * y; } //----------手を進める関数--------// //個々のコマンド bool Move(int hacmstat[], int hacm, int end, int pl) { int i; int start = hacmstat[hacm] % 50; if(CanMove(hacmstat, hacm, end, pl, false)) { //敵がいたら駒回収 int endstat = PlaceStat(hacmstat, end); //テームスを取った場合、場所を入れ替えて相手の張っている駒を除去 if(endstat == 28) { hacmstat[28] += start - end; for(i = 0; i < HACMNUM; ++i) { if(hacmstat[i] == 99 + 100 * (1 - pl)) { hacmstat[i] = 249 + 50 * (1 - pl); break; } } } else if(endstat > -1) { if(hacm == 28) { //テームスで取った場合、テームスに張っていた駒を除去 for(i = 0; i < HACMNUM; ++i) { if(hacmstat[i] == 99 + 100 * pl) { hacmstat[i] = 249 + 50 * pl; break; } } } //取った駒は持ち駒に hacmstat[endstat] = 49 + 100 * pl; } //自駒移動 hacmstat[hacm] += end - start; return true; } return false; } //コマンドの形式(棋譜の記録にも流用) //1.駒の移動 //(移動したい駒の位置)(移動する位置)[随伴する駒の位置] //"7473"なら7のteeveから7のsatiiへ //随伴したい場合は随伴したい駒の位置を付加して"464536"のように書く。 //2.方向転換 //(方向転換する駒の位置) //3.テームス解除 //- //4.テームスに張るとき //(張る駒の種類) //張る駒の種類は数字で指定する //0:横 1:縦 2:斜 3:塔 4:跳 5:色 6:扉 //返り値が0の場合エラー、その他の場合成功。 //lはコマンドの長さ、sornは手番のプレイヤー、vpは視点プレイヤー int ExecCommand(const char* c, int hacmstat[], int sorn, int vp) { int i, flag; int l = strlen(c); //1.駒の移動 if( l == 4 || l == 6 ) { int x, y, xn, yn; int p, m; int id; x = c[0] - '0'; y = c[1] - '0'; xn = c[2] - '0'; yn = c[3] - '0'; if( x < 1 || y < 1 || xn < 1 || yn < 1 || x > 7 || y > 7 || xn > 7 || yn > 7 ) { printf("位置は1から7の数字の組み合わせで指定してください"); return 0; } p = Place(x, y, vp); //移動させる駒の現在地 id = PlaceStat(hacmstat, p); //移動させる駒の番号 if( id == -1 ) { printf("その位置に駒は存在しません"); return 0; } if( id > 13 - 14 * sorn && 28 - 14 * sorn > id ) { printf("それは敵の駒です"); return 0; } if( id == 28 ) { flag = 0; for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] == 99 + 100 * sorn ) { flag = 1; break; } } if(flag == 0) { printf("テームスに駒を張っていないので移動できません"); return 0; } } m = Place(xn, yn, vp); //移動先位置 //随伴なしの場合 if( l == 4 ) { if(Move(hacmstat, id, m, sorn)) return 1; } //随伴ありの場合 if( l == 6 ) { int xz, yz, pz, idz; xz = c[4] - '0'; yz = c[5] - '0'; if( xz < 1 || yz < 1 || xz > 7 || yz > 7 ) { printf("位置は1から7の数字の組み合わせで指定してください"); return 0; } pz = Place(xz, yz, vp); idz = PlaceStat(hacmstat, pz); if( idz == -1 ) { printf("その位置に駒は存在しません"); return 0; } if( idz > 13 - 14 * sorn && 28 - 14 * sorn > idz ) { printf("敵の駒は随伴できません"); return 0; } if(!( ( p == m-1 && p%7 < 6 ) || p == m-7 || ( p == m+1 && p%7 > 0 ) || p == m+7 ) ) { printf("隣接していない駒は随伴できません"); return 0; } if( m == pz || m - p == p - pz || m - pz == pz - p || m - p == 2 * (p - pz) ) { printf("随伴する駒がある方向には随伴移動できません"); return 0; } if(CanMove(hacmstat, id, m , sorn, true) && CanMove(hacmstat, idz, pz + m - p, sorn, true)) { hacmstat[id] += m - p; hacmstat[idz] += m - p; //随伴移動は平行移動 return 1; } } printf("その位置には移動できません"); return 0; } //2.方向転換 if( l == 2 ) { int x, y, p, id; x = c[0] - '0'; y = c[1] - '0'; if( x < 1 || y < 1 || x > 7 || y > 7 ) { printf("位置は1から7の数字の組み合わせで指定してください"); return 0; } p = Place(x, y, vp); id = PlaceStat(hacmstat, p); if( id == -1 ) { printf("その位置に駒は存在しません"); return 0; } if( id > 13 - 14 * sorn && 28 - 14 * sorn > id ) { printf("敵の駒は方向転換できません"); return 0; } if(HACMKIND[id] > 3) { printf("後衛の駒は方向転換できません"); return 0; } if(hacmstat[id] % 100 < 49) { hacmstat[id] += 50; } else { //既に方向転換されている場合、元に戻す hacmstat[id] -= 50; } return 1; } if( l == 1 ) { //3.テームスから駒をはがす if( c[0] == '-' ) { for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] == 99 + 100 * sorn ) { hacmstat[i] = 49 + 100 * sorn; return 1; } } printf("テームスに駒は張られていません"); return 0; } //4.テームスに駒を張る int kind = c[0] - '0'; if( kind < 0 || kind > 6 ) { printf("テームスに張る駒の種類は以下の数字で指定してください\n0:横、1:縦、2:斜、3:塔、4:跳、5:色、6:扉"); return 0; } int flag = 0; int it = 0; for( i = 0; i < HACMNUM; ++i ) { if( HACMKIND[i] == kind && hacmstat[i] == 49 + 100 * sorn ) { hacmstat[i] += 50; flag = 1; it = i; break; } } if(flag == 1) { //元から張ってあった場合、はがす(張り替え) for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] == 99 + 100 * sorn && i != it ) { hacmstat[i] -= 50; break; } } return 1; } printf("その駒は持っていないので張れません"); return 0; } printf("書式に無茶があります"); return 0; } //棋譜書き込み。ExecCommandで正しいコマンドであることを認識してから使うこと //一手ごとに毎回開きなおすのは非効率だろうか void WriteRecord(char* c, int vp, char* filename) { int i; FILE *fp; char s[256]; int l = strlen(c); if ((fp = fopen(filename, "a")) == NULL) { printf("棋譜ファイル'%s'に書き込めません\n", filename); return; } if( l < 7 ) { if( vp == 1 && l != 1) { for( i = 0; i < strlen(c); ++i ) c[i] = '7' - c[i] + '1'; } fprintf(fp, "%s\n", c); } else { struct tm *date; time_t now; int year, month, day; int hour, minute, second; /* 現在の日時を取得 */ time(&now); date = localtime(&now); year = date->tm_year + 1900; month = date->tm_mon + 1; day = date->tm_mday; hour = date->tm_hour; minute = date->tm_min; second = date->tm_sec; if(strchr(c, ':') != NULL) //cはプレイヤーがCPUかPCか、CPUの難易度やプレイヤー名など fprintf(fp, "\nkit:%d/%d/%d %d:%d:%d\n%s\n", year, month, day, hour, minute, second, c); else //cは勝利者と得点、手数 fprintf(fp, "took:%d/%d/%d %d:%d:%d\n%s\n", year, month, day, hour, minute, second, c); } fclose(fp); return; } //駒の名前を表示する void PrintName(int hacmid) { printf("%s", NAMES[hacmid]); printf("(%s) ", KINDNAMES[HACMKIND[hacmid]]); } //現在の状態を出力する関数 //int* hacmstat: 駒の状態を表すサイズ29のint型配列。 //int sastnum: 手番の数。第何手か void PrintStat(int hacmstat[], int sastnum, int vp) { int i, j, k; int flag; printf("第%d手\n", sastnum); flag = 0; printf("\n相手持ち駒:"); for( i = 0; i < HACMNUM; ++i ) { if(hacmstat[i] == 49 + (1-vp)*100) { PrintName(i); ++flag; } } if(flag == 0) printf("なし"); flag = 0; printf("\nテームス:"); for( i = 0; i < HACMNUM; ++i ) { if(hacmstat[i] == 99 + (1-vp)*100) { PrintName(i); ++flag; break; } } if(flag == 0) printf("なし"); //盤面描画開始 printf("\n__1__2__3__4__5__6__7\n┌──┬──┬──┬──┬──┬──┬──┐\n"); for( i = 0; i < 7; ++i ) { printf("│"); for( j = 0; j < 7; ++j ) { for( k = 0; k < HACMNUM; ++k ) { flag = 0; //(i, j)に駒があったかいなか if(hacmstat[k] % 50 == Place(j+1, i+1, vp)) { if(vp == 0) { if(hacmstat[k] < 50) printf("▲"); else if(hacmstat[k] < 100) printf("■"); else if(hacmstat[k] < 150) printf("▽"); else if(hacmstat[k] < 200) printf("□"); else if(hacmstat[k] < 250) printf("●"); else printf("◆"); } else { if(hacmstat[k] < 50) printf("▼"); else if(hacmstat[k] < 100) printf("■"); else if(hacmstat[k] < 150) printf("△"); else if(hacmstat[k] < 200) printf("□"); else if(hacmstat[k] < 250) printf("●"); else printf("◆"); } printf("%s", KINDNAMES[HACMKIND[k]]); flag = 1; break; } } if( flag == 0 ) { printf("__"); } printf("│"); } printf("%s\n", SOOMNAMES[i]); } printf("└──┴──┴──┴──┴──┴──┴──┘ \n"); //盤面描画ここまで flag = 0; printf("\n自分持ち駒:"); for( i = 0; i < HACMNUM; ++i ) { if(hacmstat[i] == 49 + vp*100) { PrintName(i); ++flag; } } if(flag == 0) printf("なし"); flag = 0; printf("\nテームス:"); for( i = 0; i < HACMNUM; ++i ) { if(hacmstat[i] == 99 + vp*100) { PrintName(i); ++flag; break; } } if(flag == 0) printf("なし"); printf("\n"); } //----------以上は内部動作----------// //-----------以下はAI関係-----------// //位置による価値の増減を返す int PlaceEval(int hacmstat[], int place, int vp) { //中央ほど高い return (12 - (place / 7 - 3) * (place / 7 - 4) - (place % 7 - 3) * (place % 7 - 4)) / 2; } //駒の価値を返す int HacmEval(int hacmstat[], int hacm, int vp) { int hstat = hacmstat[hacm] % 100; int eval = 0; //方向転換していない場合 if(hstat < 49) { switch(HACMKIND[hacm]) { case 0: eval += 80; break; case 1: eval += 120; break; case 2: eval += 250; break; case 3: eval += 200; break; case 4: eval += 500; break; case 5: eval += 900; break; case 6: eval += 1100; break; case 7: eval += 4000; break; } eval += PlaceEval(hacmstat, hacmstat[hacm] % 50, vp); } //敵の持ち駒ないしテームス else if(hstat % 50 == 49) { switch(HACMKIND[hacm]) { case 0: eval -= 80; break; case 1: eval -= 120; break; case 2: eval -= 250; break; case 3: eval -= 200; break; case 4: eval -= 500; break; case 5: eval -= 900; break; case 6: eval -= 1100; break; case 7: eval -= 4000; break; } eval -= PlaceEval(hacmstat, hacmstat[28] % 50, vp); } //方向転換している場合 else { switch(HACMKIND[hacm]) { case 0: eval += 120; break; case 1: eval += 80; break; case 2: eval += 300; break; case 3: eval += 150; break; } eval += PlaceEval(hacmstat, hacmstat[hacm] % 50, vp); } return eval; } //局面評価関数 //プレイヤーvpから見たときの局面評価 int Evaluate(int hacmstat[], int vp) { int i; int eval = 0; //駒の価値 for(i = 14 * vp; i < 14 + 14 * vp; ++i) { eval += HacmEval(hacmstat, i, vp); } for(i = 14 - 14 * vp; i < 28 - 14 * vp; ++i) { eval -= HacmEval(hacmstat, i, vp); } return eval; } //手の保存用 void WriteMove(char* c, int start, int end) { sprintf(c, "%d%d%d%d", start%7+1, start/7+1, end%7+1, end/7+1); } void WriteAccompany(char* c, int start, int end, int acc) { sprintf(c, "%d%d%d%d%d%d", start%7+1, start/7+1, end%7+1, end/7+1, acc%7+1, acc/7+1); } void WriteRotate(char* c, int place) { sprintf(c, "%d%d", place%7+1, place/7+1); } void WriteDrop(char* c, int hacm) { sprintf(c, "%d", HACMKIND[hacm]); } //ネガマックス法 //depthは10倍単位 int Negamax(char* c, int hacmstat[], int pl, int vp, int depth, int depthMax) { if( depth >= depthMax ) return Evaluate(hacmstat, pl); //相手が王手となる場合終端 if(Check(hacmstat, 1 - pl)) return 20000; int check = Check(hacmstat, pl); if(check > 0 && Checkmate(hacmstat, pl, check)) return 20000 * ((vp + pl) % 2); int i, j, ii, a, b, p, ps, k; int dm; //変化後の深さ最大値。1回に1以上伸ばすと無限ループに陥る可能性があるので注意 int value = -10000; int peripheral[] = {-7, -1, 1, 7}; int tempstat[HACMNUM]; //手生成 for( i = 0; i < HACMNUM; ++i ) { if( hacmstat[i] / 100 == pl || hacmstat[i] > 199) { if( hacmstat[i] % 50 < 49 ) { for( j = 0; j < 49; ++j ) { dm = depthMax; if(CanMove(hacmstat, i, j, pl, false)) { //単独移動させる for(ii = 0; ii < HACMNUM; ++ii) tempstat[ii] = hacmstat[ii]; if( PlaceStat(hacmstat, j) > -1 ) dm += 2; //敵の駒を取る手は深さを伸ばす if(Check(hacmstat, 1 - pl)) dm += 2; Move(tempstat, i, j, pl); k = -Negamax(c, tempstat, 1 - pl, vp, depth + 10, dm); if( k > value ) { value = k; if(depth == 0) WriteMove(c, hacmstat[i] % 50, j); } //随伴移動させる if(CanMove(hacmstat, i, j, pl, true)) { dm = depthMax; for( ii = 0; ii < 4; ++ii ) { p = hacmstat[i] % 50 + peripheral[ii]; ps = PlaceStat(hacmstat, p); if( ps > -1 ) { if(!( ( p == j-1 && p%7 < 6 ) || p == j-7 || ( p == j+1 && p%7 > 0 ) || p == j+7 ) ) { continue; } if( CanMove(hacmstat, ps, j + peripheral[ii], pl, true) ) { a = hacmstat[i]; b = hacmstat[ps]; dm += 1; //随伴移動は深めに探索 if(Check(hacmstat, 1 - pl)) dm += 2; k = -Negamax(c, hacmstat, 1 - pl, vp, depth + 10, dm); hacmstat[i] = a; hacmstat[ps] = b; if( k > value ) { value = k; if(depth == 0) WriteAccompany(c, a % 50, j, p); } } } } } } } //方向転換させる if( HACMKIND[i] < 4 ) { dm = depthMax; a = hacmstat[i]; if( a % 100 < 49 ) hacmstat[i] += 50; else hacmstat[i] -= 50; if(Check(hacmstat, 1 - pl)) dm += 2; k = -Negamax(c, hacmstat, 1 - pl, vp, depth + 10, dm); hacmstat[i] = a; if( k > value ) { value = k; if(depth == 0) WriteRotate(c, a % 50); } } } //テームスに駒を張る else if( hacmstat[i] == 49 + 100 * pl ) { dm = depthMax; a = hacmstat[i]; p = -1; for( j = 14 - 14 * pl; j < 27 - 14 * pl ; ++j ) { if( hacmstat[j] == 99 + 100 * pl ) { p = j; b = hacmstat[j]; hacmstat[j] -= 50; break; } } hacmstat[i] += 50; if(Check(hacmstat, 1 - pl)) dm += 2; k = -Negamax(c, hacmstat, 1 - pl, vp, depth + 10, dm); hacmstat[i] = a; if( p > -1 ) hacmstat[p] = b; if( k > value ) { value = k; if(depth == 0) WriteDrop(c, i); } } //テームスをはがす else if( hacmstat[i] == 99 + 100 * pl ) { dm = depthMax; a = hacmstat[i]; hacmstat[i] -= 50; dm -= 2; //テームス解除は消極的に k = -Negamax(c, hacmstat, 1 - pl, vp, depth + 10, dm); hacmstat[i] = a; if( k > value ) { value = k; if(depth == 0) c = "-"; } } } } return value; } //イージーモード void EasyMode(char *c, int hacmstat[], int pl, int vp) { int i; int tempstat[HACMNUM]; for(i = 0; i < HACMNUM; ++i) tempstat[i] = hacmstat[i]; int eval = Evaluate(tempstat, vp); printf("現在の局面評価:%d\n", eval); printf("最善手後の局面評価:%d\n", Negamax(c, tempstat, pl, vp, 0, 20)); printf("最善手:%s\n", c); }