私が書いたコードは、大まかな流れを説明しようと思って、超適当に作っただけなので、私のコードを完成させる事に注力しない方が良いと思います。
私が書いた超適当なコードは、バグも含んでいるみたいですし、その様なコードを完成させる為に無駄な努力はしなくて良いと思います。
飽くまで、斜め読みして雰囲気を掴む程度に留めておいた方が良いと思います。
今見たら、usao氏が指摘している様に、既に大学の先生からベースとなるコードが提示されているみたいですね。
そちらに加筆・訂正をした方が宜しいかと思います。
リンク切れするらしいので、コード部分だけ引用して載せておきます。 (私はまだ読んでいません。)
↓
http://pr.cei.uec.ac.jp/kobo2015/index. ... bo2015.zip より引用。
header.h
コード:
#pragma once
#define DELIM " "
#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
# define strtok_r strtok_s
#endif
#ifdef SRC_BOARD
#define BOARD_EXTERN
#else
#define BOARD_EXTERN extern
#endif
/* 便利定数 */
enum {
white = 0,
black = 1,
hand = -1, /* 駒台の番号 */
MOVE_NULL = 0, /* 着手無し */
};
/* 駒の種類 */
enum {
piyo = 1,
zou = 2,
kirin = 3,
lion = 4,
niwa = 5,
/* 先手(white)の駒の種類 */
white_piece = 0,
w_piyo = white_piece + piyo,
w_zou,
w_kirin,
w_lion,
w_niwa,
/* 後手(black)の駒の種類 */
black_piece = 8,
b_piyo = black_piece + piyo,
b_zou,
b_kirin,
b_lion,
b_niwa,
/* 全ての駒の数 */
all_piece_num,
/* 駒以外の情報 */
types_num = 5,
empty = 0,
wall = 15,
};
/* 局面を表す構造体 */
typedef struct tree {
int board[32]; /* 盤上の駒の情報 */
int whand[types_num]; /* 先手の持ち駒 */
int bhand[types_num]; /* 後手の持ち駒 */
int turn; /* 現在の手番 */
int ply; /* 現在の手数 */
} tree_t;
typedef struct move {
int to; /*動いた先のマスの番号 */
int from; /* 元のマスの番号。打った駒はhand(-1)番 */
int type; /* 指した、打った駒の種類 */
int catch_type; /* 取った駒の種類 */
int is_promote; /* 1なら成った */
} move_t;
/* ゲーム局面ーと移動の履歴を表す変数 */
BOARD_EXTERN tree_t game;
BOARD_EXTERN move_t history[512];
/* board.c */
void init_board();
void make_move(move_t move);
void unmake_move(move_t move);
int is_legal(move_t move);
int is_drop_legal(move_t move);
int is_move_legal(move_t move);
int attacks_to(int from, int to, int type);
int gen_legal(move_t moves[]);
/* io.c */
void show_turn();
void show_board();
void show_hand(int turn);
void show_position();
void manual_move(char **last);
int CSA2Internal(const char *str, move_t *move);
void back();
/* search.c */
int search_root(int depth);
int evaluate();
board.c
コード:
#define SRC_BOARD
#include "header.h"
int dir_index[8] = {
-6, -5, -4, -1, 1, 4, 5, 6 };
int direction[16][8] = { //各駒の利きの向き
{ 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0 }, //W_PIYO
{ 1, 0, 1, 0, 0, 1, 0, 1 }, //W_ZOU
{ 0, 1, 0, 1, 1, 0, 1, 0 }, //W_KIRIN
{ 1, 1, 1, 1, 1, 1, 1, 1 }, //W_LION
{ 1, 1, 0, 1, 1, 1, 1, 0 }, //W_NIWA
{ 0 },
{ 0 },
{ 0 },
{ 0, 0, 0, 0, 1, 0, 0, 0 }, //B_PIYO
{ 1, 0, 1, 0, 0, 1, 0, 1 }, //B_ZOU
{ 0, 1, 0, 1, 1, 0, 1, 0 }, //B_KIRIN
{ 1, 1, 1, 1, 1, 1, 1, 1 }, //B_LION
{ 0, 1, 1, 1, 1, 0, 1, 1 }, //B_NIWA
};
int get_pro(int piece);
int get_nopro(int piece);
int drop_type(int piece);
void flip_turn();
/**
* 盤面を初期化する関数
*/
void init_board() {
int i;
for (i = 0; i <= 25; ++i) game.board[i] = wall;
game.board[6] = b_zou;
game.board[11] = b_lion;
game.board[16] = b_kirin;
game.board[12] = b_piyo;
game.board[19] = w_zou;
game.board[14] = w_lion;
game.board[9] = w_kirin;
game.board[13] = w_piyo;
game.board[7] = empty;
game.board[8] = empty;
game.board[17] = empty;
game.board[18] = empty;
for (i = 0; i<types_num; ++i) {
game.whand[i] = 0;
game.bhand[i] = 0;
}
game.ply = 1;
}
/**
* 移動処理を行う
*
* @param move 実行する移動の情報
*/
void make_move(move_t move) {
int type, captype;
int* myhand;
/* TODO: */
/** MEMO
* 1. 手番の処理
* 2. 持ち駒処理
* 1. 打つ駒を駒台から減らす
* 2. 打つ先に駒を置く
* 3. 駒を動かす指し手
* 1. (駒があれば)移動先の相手の駒を取る
* 2. 移動先に駒を置く。もし成るならば成った駒を置く
* 3. 移動元の駒を消す
* 4. 手番の移行処理
*/
}
/**
* 実行した移動を取り消す
*
* @param move 取り消す移動の情報
*/
void unmake_move(move_t move) {
int type, captype;
int* myhand;
/* TODO: */
/** MEMO
* 基本的に make_move() と逆の順番で処理をすれば良い。
* 但し、合法かどうかは判別しなくても良い(既に判定されたと考える)
*/
}
/**
* 成れる駒であれば成った後の駒を返す
*
* @param piece 成れる駒か判定する駒
*/
int get_pro(int piece) {
if (piece == w_piyo) return w_niwa;
if (piece == b_piyo) return b_niwa;
return piece;
}
/**
* 成駒であれば成る前の駒を返す
*
* @param piece 成り駒か判定する駒
*/
int get_nopro(int piece) {
if (piece == w_niwa) return w_piyo;
if (piece == b_niwa) return b_piyo;
return piece;
}
/**
* 駒台に置く駒の種類を取得する
*/
int drop_type(int piece) {
if (piece >= black_piece) return piece - black_piece;
return piece - white_piece;
}
/**
* 手番を入れ替える
*/
void flip_turn() {
/* 手番は 0=white, 1=black の2値表現 */
game.turn ^= 1;
}
/**
* 指し手が合法手であるか判別する
*
* @param move 判定する指し手情報
*/
int is_legal(move_t move) {
if (move.from == hand) return is_drop_legal(move);
else return is_move_legal(move);
}
/**
* 置駒手が合法手であるか判別する
*
* @param move 判定する置駒情報
*/
int is_drop_legal(move_t move) {
/*移動先に駒があれば打てない*/
/* TODO: */
/*持っていない駒は打てない*/
/* TODO: */
return 1;
}
/**
* 移動手が合法手であるか判別する
*
* @param move 判定する移動情報
*/
int is_move_legal(move_t move) {
int type;
/* TODO: */
/** MEMO
* 1. 移動元の駒の種類が着手と一致しているか
* 2. 成れない位置で成っていないか
先手が成れるのは,最上段の6, 11, 16
後手が成れるのは,最下段の9, 14, 19
* 3. 移動元の駒の利きでない場所ではないか
* 4. 移動先に自分の駒はないか
* 5. 移動先は壁ではないか
*/
/* */
return 1;
}
/**
* 駒の利きを判定する
*
* @param from 駒の移動前の位置
* @param to 駒の移動後の位置
* @param type 駒の種類
*/
int attacks_to(int from, int to, int type) {
int dir, i;
dir = to - from;
/* 利きのリストに含まれる方向への移動か */
/* TODO: */
/* 移動先は利きに含まれていない */
/* TODO: */
return 1;
}
/**
* 合法手を生成する関数
*
* @param moves 移動情報を格納する配列
*/
int gen_legal(move_t moves[]) {
int nmove = 0;
if (game.turn == white) {
/* ライオンが取られていたら合法手無し */
if (game.bhand[lion] == 1) return 0;
/* 駒を打つ合法手を生成 */
nmove = gen_legalhand_w(moves, nmove);
/* 駒を動かす合法手を生成 */
nmove = gen_legalmove_w(moves, nmove);
}
else {
/* ライオンが取られていたら合法手無し */
if (game.whand[lion] == 1) return 0;
/* 駒を打つ合法手を生成 */
nmove = gen_legalhand_b(moves, nmove);
/* 駒を動かす合法手を生成 */
nmove = gen_legalmove_b(moves, nmove);
}
return nmove;
}
int gen_legalhand_w(move_t moves[], int nmove) {
int i, j;
move_t move;
/* 初期化 */
move.from = hand; move.is_promote = 0; move.catch_type = 0;
/* 駒を打てる場所それぞれに対して合法手を生成 */
/* TODO: */
return nmove;
}
int gen_legalhand_b(move_t moves[], int nmove) {
int i, j;
move_t move;
/* 初期化 */
move.from = hand; move.is_promote = 0; move.catch_type = 0;
/* 駒を打てる場所それぞれに対して合法手を生成 */
/* TODO: 上と同様に実装する */
return nmove;
}
int gen_legalmove_w(move_t moves[], int nmove) {
int i, j;
move_t move;
/* 盤上の自分の駒それぞれに対して合法手を生成 */
/* TODO: */
/* MEMO:
* 1. 1駒ずつ合法手を生成していく
* 2. 利きの先が移動できない場所なら無視
* 3. 移動する指し手を生成
* 4. 最奥ならば成る手も生成
*/
return nmove;
}
int gen_legalmove_b(move_t moves[], int nmove) {
int i, j;
move_t move;
/* 盤上の自分の駒それぞれに対して合法手を生成 */
/* TODO: 上と同様に実装する */
return nmove;
}
io.c
コード:
#include "header.h"
#include <stdio.h>
#include <string.h>
/** 表示に関する関数群 **************/
/* 持ち駒として駒の種類を表示する為のテキストデータ */
const char *ch_piece[16] = {
"--", "ひ", "ぞ", "き", "ら",
"--", "に", "--", "--", "--" };
/* 盤上の駒を表示する為のテキストデータ */
const char *ch_piece2[16] = {
" ", "^ひ", "^ぞ", "^き",
"^ら", "^に", "---", "---",
" ", "vひ", "vぞ", "vき",
"vら", "vに", "---", "---" };
const char *ch_piece_csa[16] = {
"--", "HI", "ZO", "KI", "RA",
"--", "NI", "--", "--", "--", };
/* 持ち駒の表示 */
void show_turn() {
char sturn[2][8] = { "先手", "後手" };
printf("%sの番です。\n", sturn[game.turn]);
}
/* 盤面とその上の駒を表示 */
void show_board() {
int i, j, type;
/* 列表示 */
printf(" A B C\n");
for (i = 0; i<4; ++i) {
/* 横線と段表示 */
printf(" -------------\n");
printf("%d |", i + 1);
for (j = 0; j<3; ++j) { /* 駒の種類を特定してそれぞれ表示 */
type = game.board[16 - 5 * j + i];
printf("%s|", ch_piece2[type]);
}
/* 1段分の出力が終了 */
printf("\n");
}
/* 盤面の一番下の線 */
printf(" -------------\n");
}
/* 持ち駒の表示 */
void show_hand(int turn) {
int i, *hand;
/* 指定された手番の持ち駒の配列を取得 */
if (turn == white) hand = game.whand;
else hand = game.bhand;
/* 指定された手番の持ち駒を、あるものだけ表示 */
for (i = 1; i <= 4; ++i) {
if (hand[i] > 0)
printf(" %s%d", ch_piece[i], hand[i]);
}
printf("\n");
}
void show_legalmove() {
int i, nmove = 0;
move_t moves[64];
/* gen_legal() 実装後、これを利用して合法手を表示 */
for (i = 0; i<nmove; ++i) {
printf("%d>%d[%s], ", moves[i].from, moves[i].to, ch_piece_csa[moves[i].type % 8]);
}
printf("\n");
}
void show_position() {
show_turn();
show_hand(black);
show_board();
show_hand(white);
show_legalmove();
}
/** 入力に関する関数群 **************/
void manual_move(char **last) {
move_t move;
/* lastから着手部分を切り出す。
第一引数にNULLを入れ、前回切り出したlastからスタート */
const char *p = strtok_r(NULL, DELIM, last);
/* 着手の解析 */
printf("move: %s\n", p);
if (p == NULL || CSA2Internal(p, &move) == -1) {
printf("Invalid Move\n");
printf("Example: 2221HI+\n");
return;
}
/* 合法手の判定 */
if (is_legal(move) != 1) {
printf("Illegal Move\n");
return;
}
/* 着手の実行 */
make_move(move);
/* 盤面の更新 */
show_position();
}
/* CSA形式の表現を内部の指し手表現に変換する */
int CSA2Internal(const char *str, move_t *move) {
int fromX, fromY, toX, toY, i;
char buf[8] = { 0 }, *p;
/* 初期化 */
move->is_promote = 0; move->catch_type = 0;
strncpy(buf, str, strlen(str) + 1);
/* 文字列の形式によるガード */
if (str == NULL) return -1;
else if (strlen(str) != 6){
if (strlen(str) == 7 && str[6] == '+') {
move->is_promote = 1;
/* 成り情報を抽出したので捨てる */
buf[6] = '\0';
}
else return -1;
}
/* コマンドの解析
移動情報を読み出し */
fromX = str[0] - '0';
fromY = str[1] - '0';
toX = str[2] - '0';
toY = str[3] - '0';
/* xy座標系から添え字に変換 */
move->from = (21 - fromX * 5) + fromY - 1;
move->to = (21 - toX * 5) + toY - 1;
/* コマンドから駒の種類を読み出し */
p = buf + 4;
for (i = 0; i<10; ++i) {
/* 一致した手番付き駒種類を取得 */
if (!strcmp(p, ch_piece_csa[i])) {
move->type = (game.turn == white) ? i : i + 8;
break;
}
}
/* 該当する駒無し */
if (i == 10) return -1;
/* 持ち駒を打つ指し手 */
if (fromX == 0 && fromY == 0) {
move->from = hand;
return 0;
}
/* 駒を動かす指し手
取る駒を計算 */
if (move->from<0 || move->from>25 || move->to<0 || move->to >25) return -1;
move->catch_type = game.board[move->to];
return 0;
}
void back() {
if (game.ply == 1) return;
/* ひとつ前の手数の着手を取ってきてunmake_moveする。 */
unmake_move(history[game.ply - 1]);
/* 状態を書き換えたら新しい盤面を表示する。 */
show_position();
}
search.c
コード:
#include "header.h"
/**
* 探索関数のルート関数
*
* @param depth 読む手の深さ
*/
int search_root(int depth) {
int nmove, i, best, max, val;
move_t moves[64];
max = -999999;
/* TODO: */
/* MEMO:
* ここでは search() と同様に探索した後に最善手を選ぶ処理を入れる
*/
return max;
}
/**
* 探索関数の再帰関数
*
* @param depth 読む手の深さ
*/
int search(int depth) {
int i, val, max, nmove;
move_t moves[64];
max = -9999999;
// TODO:
return max;
}
/**
* 駒の評価関数
*/
int evaluate_piece() {
int i;
int score = 0;
/* TODO: */
return game.turn == white ? score : -score;
}
/**
* 盤面全体の評価関数
*/
int evaluate() {
int i;
/* TODO: */
return 0;
}
main.c
コード:
#include "header.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEFAULT_DEPTH 7
int cmd_prompt();
int main(void) {
init_board();
show_position();
while (cmd_prompt() == 0);
return 0;
}
/* コマンド入力を受け付ける
返り値が0のとき正常。1のとき異常 */
int cmd_prompt() {
char buf[256];
const char *token;
char *last;
/* 初期化 */
memset(buf, 0, sizeof(buf));
/* 現在の状態を表示 */
if (game.turn == black) printf("Black %d> ", game.ply);
else printf("White %d> ", game.ply);
//コマンド入力の受付. 空なら無視
fgets(buf, sizeof(buf), stdin);
if (!strcmp(buf, "\n"))
return 0;
//文字列の最後(改行文字)を空文字にして文字列扱いに。
buf[strlen(buf) - 1] = '\0';
/* コマンドと引数を切り出し */
token = strtok_r(buf, DELIM, &last);
if (token == NULL)
return 0;
/* コマンドに対応した関数の呼び出し */
if (!strcmp(token, "quit"))
return -1;
else if (!strcmp(token, "board"))
show_position();
else if (!strcmp(token, "new")) {
init_board();
show_position();
}
else if (!strcmp(token, "move"))
manual_move(&last);
else if (!strcmp(token, "back"))
back();
/* 該当コマンドが無い場合の例外処理 */
else {
printf("Invalid Command [%s]\n", token);
}
return 0;
}