5

I am writing a card game, in which I separated the "Core Logic" of the game from the UI. Communication with the player from the core logic part is done via callbacks which the UI implements and handles (in a separate thread if needed). The core logic library calls these callbacks, the UI handles them, passing an object representing the data that needs to be returned, and returns it.

What I'm currently stuck in is implementing the ability for the Core Logic to give the player a "choice" between possible actions. For example, the core logic might want the player to select from the following 3 game actions:

  • Draw 5 cards from Deck A
  • Draw 2 cards from Deck B
  • Allocate 4 points into bucket 1

The point being that each action can be pretty different from the other. How can I represent this information as an object in C#? I can think of 4 ugly ways...

  1. Just pass a list of strings containing the choice text to the UI and have the UI display them, then the user selects between them:

This is the most simplest (and very tempting) approach, but that would be mixing the UI with the Core Logic, which goes against the main idea behind the design.

  1. Pass an object containing an enum and an object, whose actual type depends on the value of the enum (The enum being something like Draw, Allocate, etc., and the object containing things like the number of cards and which deck for the Draw choice, and the number of points and what bucket for the Allocate command)

But that involves a lot of if statements and typecasting and reflection, among other problems, which is ugly. It also means that I would have to make a separate class for almost every type of choice enum, which is also ugly because each class would just be a list of values.

  1. Pass a dictionary contaning string keys and values, the content of the dictionary depending on the type of choice that one can make.

This will still involve a lot of typecasting and I also lose type safety, but at least I wouldn't have to make a class for every single choice.

  1. Pass an object containing an enum and a dynamic object containing an anonymous type whose fields depend on what the value of the enum is.

This makes handling the stuff in the UI a lot more straightforward and I wouldn't have to make multiple classes, but then I lose type safety and basic checks during compile time..

Is there any sort of pattern that people usually use when facing this problem, or should I just pick my poison and go for it? Perhaps I should rethink my entire approach to the whole thing?

In C, I would probably be using a union of structs for this sort of thing. I'm not sure how I would go about it in C#. Perhaps I'm thinking too much in C?

2
  • 1
    Is there anything in the design that would prevent you from using multiple callbacks? Similar to how an Ajax call has different callbacks for success and failure.
    – Morgen
    Commented Nov 8, 2015 at 22:28
  • I can probably add one callback per choice, if that's what you're suggesting, but then the UI would need to keep track of state (i.e. what callbacks were called, before displaying to the user the choices), and that would complicate the design a bit, I think. It's still an option but I'd need to think about it a little...
    – 9a3eedi
    Commented Nov 9, 2015 at 9:31

2 Answers 2

2

I'm for 2, but with slight modification. I wouldn't use enum. You can just as fine use GetType(). If you were to add enum to the mix, it would duplicate the type information contained within the object.

On top of that, I would create an Visitor for the Choice class. This will remove the need for reflection and give UI type-safe way to create textual representation for each choice. It will also ensure that when you add new choice type, the compiler will force you to implement all related code, like creating a textual representation for it. And I don't see how having many classes is bad. It is actually good thing, as long as those classes are cohesive.

And I don't think those objects will contain only data. They can very well be objects doing the actual action. Just put Execute() method on them, and they can be the ones drawing the cards from decks or allocating points to buckets.

5
  • Isn't typeof a static operator (i.e. resolved in compile time)? I think I would need to use GetType() in this case (which is not a problem) because the type passed to the UI is not known during compile time. Either way, it's a very good point to use types instead of enums.
    – 9a3eedi
    Commented Nov 9, 2015 at 9:33
  • @9a3eedi Right.
    – Euphoric
    Commented Nov 9, 2015 at 9:54
  • After thinking about it for a while, the Visitor pattern along with the rest of your answer sounds like the way to go, with one problem: how can I make sure that the UI executes only one of the choices that the Core has given it? I think I can work that out though.
    – 9a3eedi
    Commented Nov 9, 2015 at 16:18
  • 1
    @9a3eedi Well. UI doesn't need to execute anything. It can just pass it back to the Core Logic where it will be executed.
    – Euphoric
    Commented Nov 9, 2015 at 16:26
  • The "execute" function will still be exposed though. But I figured that I can still make it so that the UI has no way of providing the parameters it needs to execute it :) You're right. And maybe I'm obsessing about this too much..
    – 9a3eedi
    Commented Nov 9, 2015 at 18:21
2

I would either use a base class with inherited classes or an interface with concrete implementations in order to distinguish the kind of choice the user can make. Interfaces would make more sense if you needed to perform some kind of operation on the choice, but since that isn't in the scope of your question, here is an example of the former option using a base class with inheritance.

public class ChoiceOption
{
}

public class DrawChoiceType : ChoiceOption
{
    public int NumberToDraw { get; set; }
    public Deck DrawFrom { get; set; }
}

public class AllocatePointsChoiceType : ChoiceOption
{
    public int PointsToAllocate { get; set; }
    public Bucket AllocatePointsTo { get; set; }
}

With this structure, your game engine will pass the list of choices to the player and handle the choice made like this:

List<ChoiceOption> choicesAvailableToPlayer = new List<ChoiceOption>();
choicesAvailableToPlayer.Add(new DrawChoiceType() { DrawFrom = Decks.DeckA, NumberToDraw = 5 });
choicesAvailableToPlayer.Add(new DrawChoiceType() { DrawFrom = Decks.DeckB, NumberToDraw = 2 });
choicesAvailableToPlayer.Add(new AllocatePointsChoiceType () { AllocatedPointsTo = Buckets.Bucket1, PointsToAllocate = 4 });
ChoiceOption playerChose = player.Callback(choicesAvailableToPlayer);
if (playerChose is DrawChoiceType)
{
    //Do whatever needs to be done in the game engine for this selection.
}
else if (playerChose is AllocatePointsChoiceType)
{
     //Do whatever needs to be done in the game engine for this selection.
}

I'm not sure how your UI code is displaying these choice options to the user, but it would be very simple to drive the UI from this structure.

1
  • This is pretty much what I did, after following @Euphoric 's advice with the Visitor pattern. I also used an interface instead of a class, and added an "execute" function inside each class that implements the interface. The UI currently is command-line based, but that's temporary for testing. Eventually I want to move it to something like Unity or my own Game Engine, which is why I wanted something more generic than a text string.
    – 9a3eedi
    Commented Nov 10, 2015 at 13:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.