履歴を管理できるデザインパターン

C#で履歴を管理するためのデザインパターンとしては、以下のようなものがあります。

  1. Memento パターン: Mementoパターンは、オブジェクトの状態を保存し、後で復元できるようにします。履歴を管理する場合、オブジェクトの状態を保存するためのMementoと呼ばれるオブジェクトを使用し、そのMementoオブジェクトをスタックなどのデータ構造に格納して履歴を管理します。
  2. Command パターン: Commandパターンは、操作をオブジェクトとしてカプセル化することで、実行や取り消し、再実行などをサポートします。履歴を管理する場合、各操作をCommandオブジェクトとして表現し、それらを順次実行することで履歴を再現できます。
  3. Observer パターン: Observerパターンは、オブジェクトの状態変化を監視し、変化があった場合に通知する仕組みを提供します。履歴を管理する場合、オブジェクトの状態変化を監視し、変化があったらその内容を履歴に追加することで、履歴を作成します。

これらのデザインパターンは、履歴の管理という共通の問題を解決するために利用されます。どのパターンが最適かは、具体的な要件やアプリケーションの設計に依存します。

C#で将棋の手の履歴を保存し、再現する

将棋の手の履歴を保存し、再現するためのC#の例を示します。以下の例では、Mementoパターンを使用して手の履歴を管理します。

using System;
using System.Collections.Generic;

// 手の情報を保持するMementoクラス
public class ShogiMoveMemento
{
    public string Player { get; }
    public string Move { get; }

    public ShogiMoveMemento(string player, string move)
    {
        Player = player;
        Move = move;
    }
}

// 手の履歴を管理するCareTakerクラス
public class ShogiMoveHistory
{
    private Stack<ShogiMoveMemento> _mementos = new Stack<ShogiMoveMemento>();

    public void SaveMove(string player, string move)
    {
        var memento = new ShogiMoveMemento(player, move);
        _mementos.Push(memento);
    }

    public ShogiMoveMemento UndoMove()
    {
        if (_mementos.Count > 0)
        {
            return _mementos.Pop();
        }

        return null;
    }
}

// ゲームの操作を行うクラス
public class ShogiGame
{
    private string _currentPlayer;
    private ShogiMoveHistory _moveHistory = new ShogiMoveHistory();

    public void MakeMove(string player, string move)
    {
        // 手を実行
        Console.WriteLine($"Player {player} makes move: {move}");
        // 手の履歴を保存
        _moveHistory.SaveMove(player, move);
        // 次のプレイヤーへ切り替え
        _currentPlayer = player == "Player 1" ? "Player 2" : "Player 1";
    }

    public void UndoMove()
    {
        // 最後の手を取り消し
        var memento = _moveHistory.UndoMove();
        if (memento != null)
        {
            Console.WriteLine($"Undo move: {memento.Move} by {memento.Player}");
            // 現在のプレイヤーを元に戻す
            _currentPlayer = memento.Player;
        }
        else
        {
            Console.WriteLine("No moves to undo.");
        }
    }

    public void PrintCurrentPlayer()
    {
        Console.WriteLine($"Current player: {_currentPlayer}");
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        var game = new ShogiGame();

        game.MakeMove("Player 1", "7g-7f");
        game.MakeMove("Player 2", "3c-3d");
        game.MakeMove("Player 1", "8h-2b");

        game.PrintCurrentPlayer(); // 現在のプレイヤーを表示: Player 2

        game.UndoMove(); // 最後の手を取り消す: Player 1 の 8h-2b

        game.PrintCurrentPlayer(); // 現在のプレイヤーを表示: Player 1
    }
}

この例では、ShogiGame クラスがゲームの操作を担当し、ShogiMoveHistory クラスが手の履歴を管理します。ShogiMoveMemento クラスは、手の情報を保持するためのクラスです。

ShogiGame クラスの MakeMove メソッドでは、手を実行し、ShogiMoveHistory オブジェクトに手の情報を保存します。UndoMove メソッドは、最後の手を取り消して前の状態に戻します。

Main メソッドでは、手の履歴を作成し、現在のプレイヤーを表示し、最後の手を取り消した後に現在のプレイヤーを再度表示しています。

この例では単純な手の履歴の管理を示していますが、必要に応じてさらに機能を追加したり、将棋のルールに合わせて拡張することができます。

Unityで将棋の手の履歴を保存し、再現する


Unityにおいて、将棋の手の履歴を管理する方法について説明します。

Memento パターンの適用

Mementoパターンを使用して、将棋の手の履歴を管理することができます。具体的な実装方法は以下の通りです。

// 手の情報を保持するMementoクラス
public class ShogiMoveMemento
{
    public string Player { get; }
    public string Move { get; }

    public ShogiMoveMemento(string player, string move)
    {
        Player = player;
        Move = move;
    }
}

// 手の履歴を管理するCareTakerクラス
public class ShogiMoveHistory
{
    private List<ShogiMoveMemento> _mementos = new List<ShogiMoveMemento>();

    public void SaveMove(string player, string move)
    {
        var memento = new ShogiMoveMemento(player, move);
        _mementos.Add(memento);
    }

    public ShogiMoveMemento GetMoveAt(int index)
    {
        if (index >= 0 && index < _mementos.Count)
        {
            return _mementos[index];
        }
        return null;
    }

    public int GetMoveCount()
    {
        return _mementos.Count;
    }
}

// ゲームオブジェクトでの利用例
public class ShogiGameManager : MonoBehaviour
{
    private ShogiMoveHistory _moveHistory = new ShogiMoveHistory();

    public void MakeMove(string player, string move)
    {
        // 手を実行
        Debug.LogFormat("Player {0} makes move: {1}", player, move);
        // 手の履歴を保存
        _moveHistory.SaveMove(player, move);
    }

    public void ReplayMoveHistory()
    {
        for (int i = 0; i < _moveHistory.GetMoveCount(); i++)
        {
            var move = _moveHistory.GetMoveAt(i);
            if (move != null)
            {
                Debug.LogFormat("Replaying move {0}: {1} by {2}", i, move.Move, move.Player);
                // 手を再現する処理を記述する
                // (例: 将棋の盤面を更新するなど)
            }
        }
    }
}

上記の例では、ShogiMoveMemento クラスが手の情報を保持し、ShogiMoveHistory クラスが手の履歴を管理します。ShogiGameManager クラスは、ゲームオブジェクトでの利用例を示しています。

MakeMove メソッドは、手を実行し、手の履歴を保存します。ReplayMoveHistory メソッドでは、保存された手の履歴を順に再生する処理を行います。将棋の場合は、手を再現するための具体的な処理(例: 将棋盤の状態の更新)を追加する必要があります。

Unityの場合、表示やアニメーションなど、グラフィカルな要素が関与するため、将棋の手の履歴を管理するだけでなく、それを視覚的に表現する方法も検討することが重要です。

Commandパターンの適用

Unityで将棋の手の履歴を管理し、視覚的に表現するための方法として、Commandパターンを活用することができます。以下に、Commandパターンを使った手の履歴管理と再現の例を示します。

using System.Collections.Generic;
using UnityEngine;

// 手の実行と取り消しをカプセル化するCommandクラス
public abstract class ShogiMoveCommand
{
    public abstract void Execute();
    public abstract void Undo();
}

// 手の実行と取り消しを具体的に実装するConcreteCommandクラス
public class ShogiMove : ShogiMoveCommand
{
    private GameObject _piece;
    private Vector3 _targetPosition;

    public ShogiMove(GameObject piece, Vector3 targetPosition)
    {
        _piece = piece;
        _targetPosition = targetPosition;
    }

    public override void Execute()
    {
        // 手を実行する処理を記述
        _piece.transform.position = _targetPosition;
    }

    public override void Undo()
    {
        // 手を取り消す処理を記述
        _piece.transform.position = _piece.transform.position - (_targetPosition - _piece.transform.position);
    }
}

// 手の履歴を管理するInvokerクラス
public class ShogiMoveInvoker
{
    private List<ShogiMoveCommand> _moveHistory = new List<ShogiMoveCommand>();

    public void ExecuteMove(ShogiMoveCommand move)
    {
        move.Execute();
        _moveHistory.Add(move);
    }

    public void UndoMove()
    {
        if (_moveHistory.Count > 0)
        {
            var lastMove = _moveHistory[_moveHistory.Count - 1];
            lastMove.Undo();
            _moveHistory.RemoveAt(_moveHistory.Count - 1);
        }
    }
}

// ゲームオブジェクトでの利用例
public class ShogiGameManager : MonoBehaviour
{
    public GameObject shogiPiecePrefab;
    private ShogiMoveInvoker _moveInvoker = new ShogiMoveInvoker();

    public void MakeMove(Vector3 targetPosition)
    {
        // プレイヤーの手を実行
        var piece = Instantiate(shogiPiecePrefab, targetPosition, Quaternion.identity);
        var moveCommand = new ShogiMove(piece, targetPosition);
        _moveInvoker.ExecuteMove(moveCommand);
    }

    public void UndoMove()
    {
        // 最後の手を取り消す
        _moveInvoker.UndoMove();
    }
}

ShogiGameManager クラスでは、ゲームオブジェクトとしての将棋の盤面や駒を操作するためのメソッドが提供されています。MakeMove メソッドでは、プレイヤーの手を実行し、Instantiate メソッドを使って駒を生成して配置します。UndoMove メソッドでは、最後の手を取り消し、駒の位置を元に戻します。

Unityにおいては、この例のコードに加えて、ゲームオブジェクトの表示やアニメーション、ユーザーの入力などを組み合わせることで、将棋の手の履歴を視覚的に表現することができます。

オブザーバーパターンを活用

Unityで将棋の手の履歴を管理し、視覚的に表現するための別の方法として、Observerパターンを活用することができます。以下に、Observerパターンを使った手の履歴管理と再現の例を示します。

using System.Collections.Generic;
using UnityEngine;

// 手の履歴を管理するSubjectクラス
public class ShogiMoveHistorySubject
{
    private List<string> _moveHistory = new List<string>();
    private List<IShogiMoveHistoryObserver> _observers = new List<IShogiMoveHistoryObserver>();

    public void AddMove(string move)
    {
        _moveHistory.Add(move);
        NotifyObservers(move);
    }

    public void RegisterObserver(IShogiMoveHistoryObserver observer)
    {
        _observers.Add(observer);
    }

    public void UnregisterObserver(IShogiMoveHistoryObserver observer)
    {
        _observers.Remove(observer);
    }

    private void NotifyObservers(string move)
    {
        foreach (var observer in _observers)
        {
            observer.OnMoveAdded(move);
        }
    }
}

// 手の履歴の変更を監視するObserverインターフェース
public interface IShogiMoveHistoryObserver
{
    void OnMoveAdded(string move);
}

// 手の履歴を表示するオブザーバークラス
public class ShogiMoveHistoryObserver : MonoBehaviour, IShogiMoveHistoryObserver
{
    public void OnMoveAdded(string move)
    {
        Debug.LogFormat("Move added: {0}", move);
        // 手の履歴を視覚的に表現する処理を記述
    }
}

// ゲームオブジェクトでの利用例
public class ShogiGameManager : MonoBehaviour
{
    private ShogiMoveHistorySubject _moveHistorySubject = new ShogiMoveHistorySubject();

    public void MakeMove(string move)
    {
        // プレイヤーの手を実行
        _moveHistorySubject.AddMove(move);
    }

    public void RegisterMoveHistoryObserver(IShogiMoveHistoryObserver observer)
    {
        _moveHistorySubject.RegisterObserver(observer);
    }

    public void UnregisterMoveHistoryObserver(IShogiMoveHistoryObserver observer)
    {
        _moveHistorySubject.UnregisterObserver(observer);
    }
}

この例では、ShogiMoveHistorySubject クラスが手の履歴を管理し、IShogiMoveHistoryObserver インターフェースが手の履歴の変更を監視する役割を持ちます。ShogiMoveHistoryObserver クラスは手の履歴の変更を受け取り、視覚的な表現を行うオブザーバークラスです。

ShogiGameManager クラスでは、手の実行やオブザーバーの登録・解除を行います。MakeMove メソッドでプレイヤーの手を実行し、RegisterMoveHistoryObserver メソッドで手の履歴の変更を監視するオブザーバーを登録します。

Unityにおいては、ShogiMoveHistoryObserver クラスで手の履歴を視覚的に表現する処理を記述することで、手の履歴の変更をリアルタイムに表示したり、アニメーションやエフェクトを追加したりすることができます。

履歴情報から手を戻す

以下は、Unityで棋譜ファイルの履歴情報を管理し、手を戻す処理を行うサンプルコードです。コメントで処理の説明を追加しています

using UnityEngine;
using System.Collections.Generic;

public class Move
{
    public string MoveString;
    public float TimeElapsed;

    public Move(string moveString, float timeElapsed)
    {
        MoveString = moveString;
        TimeElapsed = timeElapsed;
    }
}

public class GameHistory : MonoBehaviour
{
    private List<Move> moves = new List<Move>();

    // 棋譜の履歴に指し手と消費時間を追加する
    public void AddMove(string moveString, float timeElapsed)
    {
        Move move = new Move(moveString, timeElapsed);
        moves.Add(move);
    }

    // 最後の手を戻す
    public void UndoMove()
    {
        if (moves.Count > 0)
        {
            // 最後の手を取り出す
            Move lastMove = moves[moves.Count - 1];
            moves.Remove(lastMove);

            // TODO: 指し手の処理を実行する
            // ...

            // TODO: 消費時間を差し引くなどの処理を実行する
            // ...
        }
    }
}

このコードでは、GameHistoryクラスが棋譜の履歴情報を管理します。AddMoveメソッドで指し手と消費時間を履歴に追加し、UndoMoveメソッドで最後の手を戻す処理を行います。

使用例として、ゲームオブジェクトにGameHistoryコンポーネントを追加し、次のように呼び出すことができます。

GameHistory gameHistory = GetComponent<GameHistory>();

// 指し手を追加する
gameHistory.AddMove("+2726FU", 12f);
gameHistory.AddMove("-3334FU", 6f);

// 最後の手を戻す
gameHistory.UndoMove();

指し手の処理や消費時間の差し引きなど、具体的な実装はコメント内にTODOとして残しています。これらの部分は、将棋ゲームのルールやゲームの実装に合わせて適切な処理を追加してください

初期画面の分析

Pで始まる行のフォーマット分析になります

“P"で始まる行のフォーマットは、将棋の局面情報を表すためのCSA形式と呼ばれる形式です。一般的なCSA形式の局面情報は、以下のルールに従っています。

  1. 行の先頭には"P"があります。
  2. “P"の後には、盤面上の各マスの駒配置情報がスペース区切りで並んでいます。
  3. 盤面上の各マスの駒配置情報は、プレイヤー側(先手または後手)と駒の種類を表す文字列で表現されます。
    • 先手の駒は小文字で表され、後手の駒は大文字で表されます。
    • 駒の種類は以下のように表されます:
      • 歩兵: “FU"
      • 香車: “KY"
      • 桂馬: “KE"
      • 銀将: “GI"
      • 金将: “KI"
      • 角行: “KA"
      • 飛車: “HI"
      • 玉将(王様): “OU"
  4. 駒が存在しないマスはアスタリスク(*)で表されます。

例えば、以下のような局面情報が含まれる行があった場合:

P1-KY-KE-GI-KI-OU-KI-GI-KE-KY
P2 * -HI *  *  *  *  * -KA *
P3-FU-FU-FU-FU-FU-FU-FU-FU-FU
P4 *  *  *  *  *  *  *  *  *
P5 *  *  *  *  *  *  *  *  *
P6 *  *  *  *  *  *  *  *  *
P7+FU+FU+FU+FU+FU+FU+FU+FU+FU
P8 * +KA *  *  *  *  * +HI *
P9+KY+KE+GI+KI+OU+KI+GI+KE+KY

上記の例では、"P"で始まる行は9行あります。各行は9つの駒配置情報を持ちます。例えば、最初の行"P1-KY-KE-GI-KI-OU-KI-GI-KE-KY"は、1段目の駒配置情報を表しています。先手の香車、桂馬、銀将、金将、玉将、金将、銀将、桂馬、香車が順に配置されています。

このように、"P"で始まる行は将棋の局面情報を表すためのCSA形式であり、駒の種類や配置情報が特定のルールに基づいて表現されています。

分析サンプル

先手には接頭に"+"、後手には接頭に"-“が付加される情報を取得するには、正規表現パターンを微調整する必要があります。以下は、修正したコードのサンプルです

using System.Text.RegularExpressions;

// 配置情報を分析するメソッド
private void AnalyzePosition(string position)
{
    // 駒のフォーマットパターン
    string piecePattern = @"([+-]?)([A-Z]{2})";
    
    // 正規表現によるマッチング
    MatchCollection matches = Regex.Matches(position, piecePattern);
    
    // マッチした駒の取得
    if (matches.Count > 0)
    {
        // 駒の一覧を表示
        foreach (Match match in matches)
        {
            string prefix = match.Groups[1].Value;
            string piece = match.Groups[2].Value;
            
            if (prefix == "+")
            {
                Debug.Log("先手駒: " + piece);
            }
            else if (prefix == "-")
            {
                Debug.Log("後手駒: " + piece);
            }
        }
    }
    else
    {
        Debug.Log("配置情報が見つかりませんでした: " + position);
    }
}

正規表現パターン([+-]?)([A-Z]{2})を使用して、駒の前に"+"または"-“があるかどうかを判定し、prefixという変数に格納しています。それに加えて、pieceには駒の文字列が格納されます。

解析結果は、先手の駒と後手の駒に分けて表示されます。例えば、以下のような駒の配置情報が入力された場合:

+KY-KE-GI-KI-OU-KI-GI-KE-KY

上記のコードを実行すると、以下の結果が表示されます:

先手駒: KY
先手駒: KE
先手駒: GI
先手駒: KI
先手駒: OU
先手駒: KI
先手駒: GI
先手駒: KE
先手駒: KY

このように、正規表現を微調整して配置情報を解析し、先手と後手の駒を取得することができます。

C#,Unity,学習,設計

Posted by hidepon