Newer
Older
ss1_TicTacToeEx / TicTacToeEx / T3EBoard.cpp
#include "T3EBoard.h"
#include <iostream>
#include <time.h>
#include <stdlib.h>

// コンストラクタ
T3EBoard::T3EBoard()
{
	srand((unsigned int)clock());
	this->Reset();
}

// デストラクタ
T3EBoard::~T3EBoard()
{
}

// ゲーム初期化
void T3EBoard::Reset()
{
	m_Turn = 1;
	m_NextPlayer = PLAYER1;
	for (int i = 0; i < BOARD_SIZE; i++) {
		m_Board[i] = NONE;
	}
}

// ゲーム進行
void T3EBoard::Play()
{
	// ゲーム初期化
	std::cout << "拡張版○× ゲーム" << std::endl << std::endl;
	std::cout << "先手は 0:人間 1:CPU ?";
	std::cin >> m_Player[PLAYER1];
	std::cout << "後手は 0:人間 1:CPU ?";
	std::cin >> m_Player[PLAYER2];
	std::cout << "始め!" << std::endl;

	// ターン進行
	do {
		// ターン情報
		std::cout << std::endl << "ターン " << m_Turn << std::endl;
		this->ShowBoard();
		std::cout << (m_NextPlayer == PLAYER1 ? "先手" : "後手")
			<< "の番です" << std::endl;

		int place = (m_Player[m_NextPlayer] == PLAYER_HUMAN ? Human() : CPU());
		this->SetStone(place);
		int winner = this->CheckWin();
		if (winner >= 0) {
			this->ShowBoard();
			std::cout << std::endl << (m_NextPlayer == PLAYER1 ? "先手" : "後手") 
				<< " 勝利" << std::endl;
			break;
		}

		// 次の番へ
		m_NextPlayer = 1 - m_NextPlayer;
		if (m_NextPlayer == PLAYER1) m_Turn ++;
		if (m_Turn >= MAX_TURNS) std::cout << std::endl << "引き分け!" << std::endl;
	} while (m_Turn < MAX_TURNS);

	std::cin.ignore(std::cin.rdbuf()->in_avail());	// 入力バッファクリア
	std::cin.get();	// 終了前のキー入力待ち
}

// CPUの思考アルゴリズム
// 戻り値:コマを打つ場所
int T3EBoard::CPU()
{
	int place = 0;
	do {
		place = rand() % BOARD_SIZE;
	} while (m_Board[place] != NONE);

	std::cout << "CPU => " << place << std::endl;
	return place;
}


//どの盤面が自分に一番大きい評価をもたらすか確認。
//引数:int node 局面, in turn 手版, int turn ターン数,int depth 先読みの深さ,int state 状態
//戻り値:評価値
int T3EBoard::Minmax(int *node, int NextPlayer, int turn, int depth,int state)
{
	//printf("depth = %dでMinMax起動\n", depth);
	//葉の場合評価値を返す
	if (depth == 0) {
		//printf("葉なので%dを返します\n", this->CheckWin(node));
		return this->CheckWin(node);
	}

	//すでに勝負がついている場合葉とみなして評価値を返す
	if(state ==1){
		//printf("既に勝負がついているので葉とみなして%dを返します\n", this->CheckWin(node));
		return this->CheckWin(node);
	}
	

	int NextBoards[BOARD_SIZE*BOARD_SIZE];
	int NextBoard[BOARD_SIZE];
	int NextNode;
	int k = 0; //次の局面格納用変数
	int val;

	//ノードを一つ進める
	//次の可能手数を数える
	NextNode = this->count_zero(node);
	//石を置いた局面を全て格納
	for (int i = 0;i < BOARD_SIZE;i++) {
		if (node[i] == 0) {
			this->SetStone(node, i, NextPlayer, turn);
			for (int j = 0;j < BOARD_SIZE;j++) {
				NextBoards[k] = node[j];
				k++;
			}
			node[i] = 0;
		}
	}


	k = 0;
	int best = INT_MIN;
	NextPlayer = 1 - NextPlayer;
	if (NextPlayer == PLAYER1)turn++;
	for (int i = 0;i < NextNode;i++) {
		for (int j = 0;j < BOARD_SIZE;j++) {
			NextBoard[j] = NextBoards[k];
			k++;
		}
		//printf("以下の盤面で下のノードいきます\n");
		//this->ShowBoard(NextBoard);

		if (CheckWin(NextBoard) == 1) {
			val = this->Minmax(NextBoard, NextPlayer, turn, (depth - 1), 1);
		}
		else {
			val = this->Minmax(NextBoard, NextPlayer, turn, (depth - 1), 0);
		}
		if (NextPlayer == PLAYER1 && best < val) {
		best = val;
			//printf("☆☆best = %d に更新されました\n", best);
		}
		if (NextPlayer == PLAYER2 && best < -val) {
			best = -val;
			//printf("☆☆best = %d に更新されました\n", best);
		}
	}


	//printf("値best = %dを返します\n", best);
	return best;
}

//配列ごとによる勝敗判定
//引数:int *Board 局面
//戻り値;0 未決着 1(PLAYER1) 先手勝利 1(PLAYER2) 後手勝利
int T3EBoard::CheckWin(int *Board)
{
	int row, col;
	bool check;
	int sign[2] = { STONE1, STONE2 };

	// プレイヤー毎に判定
	for (int player = PLAYER1; player <= PLAYER2; player++) {
		// 斜め方向1
		for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
			if (Board[row * BOARD_WIDTH + row] * sign[player] <= 0) check = false;
		}
		if (check) {
			if (player == PLAYER1)return 1;
			return 1;
		}

		// 斜め方向2
		for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
			if (Board[row * BOARD_WIDTH + (BOARD_HEIGHT - 1 - row)] * sign[player] <= 0) check = false;
		}
		if (check) {
			if (player == PLAYER1)return 1;
			return 1;
		}

		// 横方向
		for (row = 0; row < BOARD_HEIGHT; row++) {
			for (col = 0, check = true; col < BOARD_WIDTH; col++) {
				if (Board[row * BOARD_WIDTH + col] * sign[player] <= 0) check = false;
			}
			if (check) {
				if (player == PLAYER1)return 1;
				return 1;
			}
		}

		// 縦方向
		for (col = 0; col < BOARD_WIDTH; col++) {
			for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
				if (Board[row * BOARD_WIDTH + col] * sign[player] <= 0) check = false;
			}
			if (check) {
				if (player == PLAYER1)return 1;
				return 1;
			}
		}
	}

	return 0;

}

// 引数配列の可能手数を算出
// 引数:int *Board 手版
//戻り値:可能手数
int T3EBoard::count_zero(int *Board)
{
	int Count = 0;

	for (int i = 0; i < BOARD_SIZE; i++) {
		if (Board[i] == 0)Count++;
	}

	return Count;
}

//石を置く
//引数:int *Board 局面, int place 置く場所, int NextPlayer 次置く人, int turn ターン数
void T3EBoard::SetStone(int *Board, int place, int NextPlayer, int Turn) {
	int sign = NextPlayer == PLAYER1 ? STONE1 : STONE2;
	Board[place] = Turn * sign;

	// 古いコマを消す
	if (Turn > MAX_PIECE_ON_BOARD) {
		int deleting = (Turn - MAX_PIECE_ON_BOARD) * sign;
		for (int i = 0; i < BOARD_SIZE; i++) {
			if (Board[i] == deleting) Board[i] = 0;
		}
	}
}

//配列の番を表示
void T3EBoard::ShowBoard(int *Board)
{
	for (int i = 0; i < BOARD_SIZE; i++) {
		std::cout << (Board[i] == 0 ? " " :
			(Board[i] > 0 ? "○" : "×"));
		if (i % BOARD_WIDTH < BOARD_WIDTH - 1) {
			std::cout << "│";
		}
		else {
			std::cout << std::endl;
			if (i < BOARD_SIZE - 1) std::cout << "─┼─┼─" << std::endl;
		}
	}
}


// 人間の手番
// 戻り値:コマを打つ場所
int T3EBoard::Human()
{
	int place = 0;
	int NextBoard[BOARD_SIZE];
	int NextNextPlayer;
	int NextTurn = m_Turn;

	NextNextPlayer = 1 - m_NextPlayer;
	if (NextNextPlayer == PLAYER1)NextTurn++;

	for (int i = 0;i < BOARD_SIZE;i++) {
		if (m_Board[i] == 0) {
			for (int j = 0;j < BOARD_SIZE;j++) {
				NextBoard[j] = m_Board[j];
			}
			SetStone(NextBoard, i, m_NextPlayer, m_Turn);
			printf("置く場所[%d]の評価値は[%d]です。\n", i, Minmax(NextBoard, NextNextPlayer, NextTurn, 4,0));
		}
	}
	

	while (1) {
		std::cout << "0:左上 1:上 2:右上 3:左 4:中央 5:右 6:左下 7:下 8:右下" << std::endl
			<< "どこに打ちますか? ";
		std::cin >> place;
		if (place >= 0 && place < BOARD_SIZE) {
			if (m_Board[place] == 0) break;
		}
		return place;
	}
}

// 盤にコマを置く
// 引数 int place : 置く位置
void T3EBoard::SetStone(int place)
{
	int sign = m_NextPlayer == PLAYER1 ? STONE1 : STONE2;
	m_Board[place] = m_Turn * sign;

	// 古いコマを消す
	if (m_Turn > MAX_PIECE_ON_BOARD) {
		int deleting = (m_Turn - MAX_PIECE_ON_BOARD) * sign;
		for (int i = 0; i < BOARD_SIZE; i++) {
			if (m_Board[i] == deleting) m_Board[i] = 0;
		}
	}
}

// 勝利判定
// 戻り値:-1 未決着 0(PLAYER1) 先手勝利 1(PLAYER2) 後手勝利
int T3EBoard::CheckWin()
{
	int row, col;
	bool check;
	int sign[2] = {STONE1, STONE2};

	// プレイヤー毎に判定
	for (int player = PLAYER1; player <= PLAYER2; player++) {
		// 斜め方向1
		for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
			if (m_Board[row * BOARD_WIDTH + row] * sign[player] <= 0) check = false;
		}
		if (check) return player;

		// 斜め方向2
		for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
			if (m_Board[row * BOARD_WIDTH + (BOARD_HEIGHT - 1 - row)] * sign[player] <= 0) check = false;
		}
		if (check) return player;

		// 横方向
		for (row = 0; row < BOARD_HEIGHT; row++) {
			for (col = 0, check = true; col < BOARD_WIDTH; col++) {
				if (m_Board[row * BOARD_WIDTH + col] * sign[player] <= 0) check = false;
			}
			if (check) return player;
		}

		// 縦方向
		for (col = 0; col < BOARD_WIDTH; col++) {
			for (row = 0, check = true; row < BOARD_HEIGHT; row++) {
				if (m_Board[row * BOARD_WIDTH + col] * sign[player] <= 0) check = false;
			}
			if (check) return player;
		}
	}

	return -1;
}

// 盤表示
void T3EBoard::ShowBoard()
{
	for (int i = 0; i < BOARD_SIZE; i++) {
		std::cout << (m_Board[i] == 0 ? " " :
			(m_Board[i] > 0 ? "○" : "×"));
		if (i % BOARD_WIDTH < BOARD_WIDTH - 1) {
			std::cout << "│";
		}
		else {
			std::cout << std::endl;
			if (i < BOARD_SIZE - 1) std::cout << "─┼─┼─" << std::endl;
		}
	}
}