pio
pio

Reputation: 540

How to use polymorphism instead of generic in C#

I have a generic base Interface IGameble (for the example move is generic) and Classes: Chess and Tic-Tac-Toe (and more board games) which derived from the generic Interface IGmable but are no longer generic.

I want to design class Agent (there might be several kind of these) that will play any kind of game (not in the same time), but the problem is that I cant

for example:

 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}

The problems is that I want 1 instance of the random agent (or any other agent) to play all the games (1 game at a time) and using the generic enforce me to specifically choose the types of T_Move and T_Matrix that I want to use.

I might have it all wrong and used the generic poorly.

There was a suggestion to use IMovable and IBoardable instead of the generic. Is it the correct design? Would it solve all the problems?

I'm still a noobie in design pattern in C# :(

Also I will be appreciate it very much if some one can get a link to some design pattern that can help me here (if there is one..).

Upvotes: 1

Views: 213

Answers (2)

apocalypse
apocalypse

Reputation: 5884

I don't know is that what you want, but you can do it without generics.

Sample code:

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 ();
    }
}

Upvotes: 1

smead
smead

Reputation: 1808

I think you can just use the is keyword:

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

Generics in C# aren't nearly as flexible as C++, so as some commenters pointed out, you may not want to do things this way. You may want to design an intermediate interface, say IMove, that your algorithm can use to enumerate the moves in a generic way.

Upvotes: 3

Related Questions