2017-03-08 70 views
1

我有一个通用的基本接口IGameble(例如移动是通用的)和类:从通用接口IGable派生的国际象棋和井字棋(以及更多棋盘游戏)但不再是通用的。如何在C#中使用多态而不是泛型

我想设计类代理(可能有几种,这些),将发挥任何类型的游戏(不是在同一时间),但问题是,我不能

例如:

interface IGame<T_move,T_matrix> 
{ 
    T_matrix[,] game_state { get; } 
    void MakeMove(Move mov); 
    List<Move> ListAllMoves(); 
    ...// some more irrelevant code 
} 

class Tic_Tac_Toe : IGameble<Tuple<int,int>,int> 
{ 
    public int turn; 
    public int[,] game_state; 
    public List<Tuple<int,int>> ListAllMoves() {...} 
    public void MakeMove(Tuple<int,int> mov) {...} 
} 
public Chess : IGameble <some other kind> //... 
// so on more classes which uses other kind in the generic. 

interface IAgent<Move> 
{ 
    Move MakeMove(); 
} 
public RandomAgent<T_Move,T_Matrix> : IAgent 
{ 
    public IGameble<T_move> game; 
    public D(IGameble game_) {game = game_} 
    public T_Move MakeMove() {//randomly select a move} 
} 
public UserAgent<T_Move,T_Matrix> : IAgent {get the move from the user} 

的问题是,我想随机代理(或任何其他代理人)玩所有的游戏(每次1个游戏)的1个实例,并使用通用强制我专门选择的类型T_Move的和我想要使用的T_Matrix。

我可能会把它全部弄错,并使用通用性很差。

有人建议使用IMovable和IBoardable,而不是通用。这是正确的设计吗?它能解决所有问题吗?

我仍然在设计模式在C#:(一noobie

此外,我将非常感激,如果有人能得到一些设计模式的链接,可以帮助我在这里(如果有..)

+2

什么是你T上的目的是什么?我无法完全理解你想要达到的目标。你可以在你的界面中添加一些方法吗?将一些实现放在可以更加描述问题的类中。 – kat1330

+5

给一些更多的真实世界的代码。像A B C D E这样的命名会烧伤我的大脑。 – apocalypse

+4

目前还不清楚你的目标是什么。您的通用界面很可能是您为实现目标而设计不当,并且正在努力将语言适合您的不当设计 –

回答

1

我不知道是你想要什么,但你可以做到没有泛型。

示例代码:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main (string[] args) 
     { 
      var game = new TicTacToe (
       (x) => new UserAgent (x, "John"), 
       (x) => new RandomAgent (x)); 

      game.Play(); 

      Console.ReadKey(); 
     } 
    } 

    public interface IGame 
    { 
     IMove[] GetAvailableMoves(); 
    } 

    public interface IMove 
    { 
     void MakeMove(); 
     string Description { get; } 
    } 

    public interface IAgent 
    { 
     string Name { get; } 
     IMove SelectMove(); 
    } 

    public delegate IAgent AgentCreator (IGame game); 

    public class RandomAgent : IAgent 
    { 
     private readonly IGame game; 
     private readonly Random random = new Random(); 

     public RandomAgent (IGame game) 
     { 
      this.game = game; 
     } 

     public string Name => "Computer (random moves)"; 

     public IMove SelectMove() 
     { 
      var availableMoves = game.GetAvailableMoves(); 

      int moveIndex = random.Next (availableMoves.Length); 

      return availableMoves[moveIndex]; 
     } 
    } 

    public class UserAgent : IAgent 
    { 
     private readonly IGame game; 

     public UserAgent (IGame game, string playerName) 
     { 
      this.game = game; 
      Name = playerName; 
     } 

     public string Name { get; } 

     public IMove SelectMove() 
     { 
      var availableMoves = game.GetAvailableMoves(); 

      Console.WriteLine ("Choose your move:"); 

      for (int i = 0; i < availableMoves.Length; i++) 
      { 
       Console.WriteLine (i + " " + availableMoves[i].Description); 
      } 

      int selectedIndex = int.Parse (Console.ReadLine()); 

      return availableMoves[selectedIndex]; 
     } 
    } 

    public class TicTacToe : IGame 
    { 
     enum CellState { Empty = 0, Circle, Cross } 

     CellState[] board = new CellState[9]; // 3x3 board 
     int currentPlayer = 0; 

     private IAgent player1, player2; 

     public TicTacToe (AgentCreator createPlayer1, AgentCreator createPlayer2) 
     { 
      player1 = createPlayer1 (this); 
      player2 = createPlayer2 (this); 
     } 

     public void Play() 
     { 
      PrintBoard(); 

      while (GetAvailableMoves().Length > 0) 
      { 
       IAgent agent = currentPlayer == 0 ? player1 : player2; 

       Console.WriteLine ($"{agent.Name} is doing a move..."); 

       var move = agent.SelectMove(); 

       Console.WriteLine ("Selected move: " + move.Description); 

       move.MakeMove(); // apply move 

       PrintBoard(); 

       if (IsGameOver()) break; 

       currentPlayer = currentPlayer == 0 ? 1 : 0; 
      } 
      Console.Write ("Game over. Winner is = ..."); // some logic to determine winner 
     } 

     public bool IsGameOver() 
     { 
      return false; 
     } 

     public IMove[] GetAvailableMoves() 
     { 
      var result = new List<IMove>(); 

      for (int i = 0; i < 9; i++) 
      { 
       var cell = board[i]; 

       if (cell != CellState.Empty) continue; 

       int index = i; 

       int xpos = (i % 3) + 1; 
       int ypos = (i/3) + 1; 

       var move = new Move ($"Set {CurrentPlayerSign} on ({xpos},{ypos})",() => 
       { 
        board[index] = currentPlayer == 0 ? CellState.Cross : CellState.Circle; 
       }); 

       result.Add (move); 
      } 

      return result.ToArray(); 
     } 

     private char CurrentPlayerSign => currentPlayer == 0 ? 'X' : 'O'; 

     public void PrintBoard() 
     { 
      Console.WriteLine ("Current board state:"); 

      var b = board.Select (x => x == CellState.Empty ? "." : x == CellState.Cross ? "X" : "O").ToArray(); 

      Console.WriteLine ($"{b[0]}{b[1]}{b[2]}\r\n{b[3]}{b[4]}{b[5]}\r\n{b[6]}{b[7]}{b[8]}"); 
     } 
    } 

    public class Move : IMove // Generic move, you can also create more specified moves like ChessMove, TicTacToeMove etc. if required 
    { 
     private readonly Action moveLogic; 

     public Move (string moveDescription, Action moveLogic) 
     { 
      this.moveLogic = moveLogic; 
      Description = moveDescription; 
     } 

     public string Description { get; } 

     public void MakeMove() => moveLogic.Invoke(); 
    } 
} 
+0

这就是我正在寻找的代码! thx很多:D – pio

3

我想你可以只使用is关键字:

public D(IA<T> a_) 
{ 
    if (a_ is B) 
    { 
     //do something 
    } 
    else if (a_ is C) 
    { 
     //do something else 
    } 
} 

泛型在C#是几乎没有灵活的C++,因此一些评论者指出的那样,你可能不希望这样做事情就是这样,你可能想要设计一个中间接口,说IMove,你的算法可以用通用的方式枚举移动。

+0

我认为设计中间接口IMovable会比随便问一下什么样的类是我好。 虽然不知道is关键字。 so thx很多! – pio