C# – Method waits for user action

Architecturec

I have a structural problem for an application I'm working on. In the interest of full disclosure, it is for a university assignment so I'm not looking for you to code me an answer, just help me think through the best approach.

I'm building a game using an OO approach. I have an abstract class called Player from which several types derive. These represent the computer and human players. So I have a derived HumanPlayer class which must provide an implementation for a method called ChooseCard. The way the user selects a card is by clicking on a card in a GUI. So the card will be identified by the user through an event handler.

The relevant part of my code is listed below. At the moment the Game class loops through each player and calls the PlayCard method on each Player. I'm just not sure how to program the ChooseCard method in the HumanPlayer class so that it can wait for the user to select a card to play but have the UI remain responsive. Ideally I want to be able to treat each of the derived player types the same (ie as the base class Player).

This question is asking basically the same thing I am but I don't really like the answers.

The answer to this question is interesting, using the TaskCompletionSource class.

I'm not fixed on this structure if there is a superior solution although I need to keep the class structure.

Edits:
Added a class diagram for the models of my application as well as a screenshot of what the gameboard looks like (currently).

Here is my current thinking:
The user will click on the card they want to play. The click event will propagate to the bound CardViewModel (At this point the action could be handled at the Model or ViewModel layer). Now the CardViewModel (or Card) doesn't know who is interested in knowing that the user selected it. The interested party will be the PlayerViewModel/HumanPlayer to whom the Card belongs, but they will only be interested when they are expecting a response from the user. I.e. when HumanPlayer.PlayCard() is called.

So I'm thinking that the CardViewModel/Card should raise an event to which the HumanPlayer object can subscribe to when it is interested in the user making a card selection.

I can make HumanPlayer.ChooseCard() subscribe to the CardSelected events of each of the cards. I'm just not sure how to make the method wait without blocking until one of those events fires.

abstract public class Player
{
    public IReadOnlyList<Card> Cards { get { return cards.AsReadOnly(); } }

    public Card PlayCard()
    {
        var cardToPlay = ChooseCard();
        // ...
    }

    protected abstract Card ChooseCard();
}

public class HumanPlayer : Player
{
    protected override Card ChooseCard()
    {
        // TODO: request a card from the user.
        throw new NotImplementedException();
    }
}

public class Game
{
    private List<Player> livingPlayers = new List<Player>();

    public void PlayRound()
    {
        var cardsPlayedByPlayers = GetCardsPlayed();
        // ...
    }

    private Dictionary<Player, Card> GetCardsPlayed()
    {
        var cardsPlayedByPlayers = new Dictionary<Player, Card>();

        foreach (var player in livingPlayers.Where(p => !p.IsDead))
        {
            // Player.PlayCard() called here getting chosen card from the player.
            cardsPlayedByPlayers.Add(player, player.PlayCard());
        }

        return cardsPlayedByPlayers;
    }
}

Update:

I've put the final code up on GitHub here since the assignment is over. You can now see the full code clearly and if you would like to critique it, I'd be keen to hear your thoughts.

Best Answer

You have the organization of the code "inside-out". What you're trying to do is respond to a player having chosen a card but instead you're forcing it in the construction of the object which is an awkward place to block (most people don't expect constructors to be "heavy").

Here's your options within the game loop :

1) When evaluating the game loop - do so from a separate thread besides the UI thread and block that thread until the UI has finished choosing a card. This allows you to use 'chooseCard' as a method on the player object - you'd remove it from the constructor and make the method public.

2) If you evaluate the game loop on the main thread then you're not really dealing with player objects but objects that represent the input (arguably you're doing that now already). If so then you should have a delegate or callback implemented that allows the Game loop to react to a "card-chosen" event rather than blocking on a list of objects for each one to complete. Hence, the game loop would maintain in the main thread the state of all the cards chosen and only 'process them' once all participants have responded.

Of the two, the latter is probably more what you're looking for if you aren't a fan of multi-threading.