using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace TTT {
public enum PLAYER { None = 0, First = 1, Second = 2 };
public enum JUDGE { None, WIN, DRAW, OUT_OF_RANGE, OVERLAP }
/// <summary>
/// TicTacToeクラス
/// ゲーム進行のみ管理.UIは関与しない.
/// </summary>
class TicTacToe {
public readonly int BOARD_ROWS = 3; // マスの行数
public readonly int BOARD_COLS = 3; // マスの列数
public readonly int BOARD_SIZE = 9; // マスの総数
public readonly int REMAIN_PIECES = 3; // 盤に残る駒の数
public readonly int PLAYERS = 2; // プレイヤー数
public readonly int TURN_LIMIT; // ターン数上限(このターン数に至ると引き分け)
public readonly int NONE = 0; // 空のマス
public readonly int[] SIGN; // プレイヤーの符号
public readonly int[,] LINES; // 並べる線の情報
public int[] Board { get; private set; } // 盤 0:空 正:先手駒 負:後手駒
public int Turn { get; private set; } // ターン数
public PLAYER Player { get; private set; } // 手を挿すプレイヤー 1:先手 2:後手
public PLAYER Winner { get; set; } // 勝者 0:なし 1:先手 2:後手
public JUDGE Judge { get; private set; } // ゲーム終了の判定
public int LastSet { get; private set; } // 直前に置いた場所
private IntPtr _board; // アンマネージドメモリの盤データ
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="turnLimit">ターン数上限</param>
public TicTacToe(int turnLimit = 100) {
TURN_LIMIT = turnLimit;
Board = new int[BOARD_SIZE];
SIGN = new int[] { 0, 1, -1 };
LINES = new int[8, 3]{ { 0, 1, 2 }, { 3, 4, 5 }, { 6, 7, 8 },
{ 0, 3, 6 }, { 1, 4, 7 }, { 2, 5, 8 }, { 0, 4, 8 }, { 2, 4, 6 } };
_board = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(int)) * BOARD_SIZE);
}
/// <summary>
/// デストラクタ
/// </summary>
~TicTacToe() {
Marshal.FreeCoTaskMem(_board);
}
/// <summary>
/// ゲームの初期化
/// </summary>
public void Init() {
for (var i = 0; i < BOARD_SIZE; i++) Board[i] = NONE;
Turn = 1;
Player = PLAYER.First;
Winner = PLAYER.None;
Judge = JUDGE.None;
}
/// <summary>
/// 駒を置き,結果を判定
/// </summary>
/// <param name="pos">置く場所</param>
/// <returns>判定結果</returns>
public void Set(int pos) {
// 値チェック
LastSet = pos;
if (pos < 0 || pos >= BOARD_SIZE) {
Judge = JUDGE.OUT_OF_RANGE;
return;
}
// 反則(重ね置き)チェック
if (Board[pos] != 0) {
Winner = Player == PLAYER.First ? PLAYER.Second : PLAYER.First;
Judge = JUDGE.OVERLAP;
return;
}
// 盤の更新
Board[pos] = Turn * SIGN[(int)Player];
if (Turn > REMAIN_PIECES) {
var remove = (Turn - REMAIN_PIECES) * SIGN[(int)Player];
for (var i = 0; i < BOARD_SIZE; i++) {
if (Board[i] == remove) Board[i] = NONE;
}
}
// 勝利判定
for (var i = 0; i < LINES.GetLength(0); i++) {
if (Board[LINES[i, 0]] * SIGN[(int)Player] > 0 &&
Board[LINES[i, 1]] * SIGN[(int)Player] > 0 &&
Board[LINES[i, 2]] * SIGN[(int)Player] > 0) {
Winner = Player;
Judge = JUDGE.WIN;
return;
}
}
// 次へ
if (Player == PLAYER.First) {
Player = PLAYER.Second;
} else {
Player = PLAYER.First;
if (++Turn >= TURN_LIMIT) {
Judge = JUDGE.DRAW;
return;
}
}
return;
}
/// <summary>
/// アンマネージドメモリの盤データを取得
/// </summary>
/// <returns></returns>
public IntPtr GetBoard() {
Marshal.Copy(Board, 0, _board, BOARD_SIZE);
return _board;
}
}
}