2
\$\begingroup\$

I have a simple base class and two derived classes. However, when I'm creating new instances of that class, the line where I instantiate the variables is getting way too long.

I have it on two lines:

private Bot Bot1 = new Bot(10000, AnchorStyles.Left, "Bot 1", 2  (int)UsersProperties.CUser.Bot1, new Point(15, 420), false, new Tuple<int?, int>(Player.Chips, 6));

As you can see, it's really long. How can I shorten those lines? Maybe I can break up the base class into a few smaller ones. I have also read that it's not a good idea for a class to expose that many members.

The base class:

public class UsersProperties : UserControl
{
    public enum CUser //stands for currentUser
    {
        Player,
        Bot1,
        Bot2,
        Bot3,
        Bot4,
        Bot5
    }

    public int RightCard { get; set; }
    public string Name { get; set; }
    public int? Chips { get; set; }
    public int Type { get; set; }
    public bool Turn { get; set; }
    public bool FoldTurn { get; set; }
    public int PreviousCall { get; set; }
    public int LeftCard { get; set; }
    public double Power { get; set; }
    public int EnumCasted { get; set; }
}

The first derived class which contains just two overloads of the constructor:

public class Player : UsersProperties
{
    public Player(int? chips, Point cardsLocation)
    {
        LeftCard = 1;
        RightCard = 0;
        Name = "Player";
        Chips = chips;
        Type = -1;
        Power = 0;
        Turn = true;
        FoldTurn = false;
        CardsAnchor = AnchorStyles.Bottom;
        PreviousCall = 0;
        EnumCasted = (int)CUser.Player;
        CardsLocation = cardsLocation;
        UsernameLabelLocation = new Point(CardsLocation.X, CardsLocation.Y + Settings.Height);
        PanelLocation = new Point(CardsLocation.X - IndentationPanelXy, CardsLocation.Y - IndentationPanelXy);
    }
    public Player(int? chips, bool turn, bool foldTurn, AnchorStyles style, Point cardsLocation)
    {
        LeftCard = 1;
        RightCard = 0;
        Name = "Player";
        Chips = chips;
        Type = -1;
        Power = 0;
        Turn = turn;
        FoldTurn = foldTurn;
        CardsAnchor = style;
        PreviousCall = 0;
        EnumCasted = (int)CUser.Player;
        CardsLocation = cardsLocation;
        UsernameLabelLocation = new Point(CardsLocation.X, CardsLocation.Y + Settings.Height);
        PanelLocation = new Point(CardsLocation.X - IndentationPanelXy, CardsLocation.Y - IndentationPanelXy);
    }
}

Now the second derived class also contains two overloads of the constructor, but it also has some methods which implement some sort of AI. It might also be reviewed, but the main thing I want here is to break down the base class into a few smaller ones or something better (this is just my idea).

public class Bot : UsersProperties
{
    public int[] MaxRaise { get; } = new int[2];
    public static int PotText { get; set; } = MainPoker.Sb + MainPoker.Bb;
    public RngCrypto Random { get; } = new RngCrypto();

    public Bot(AnchorStyles style, string name, int rightCard, int enumCasted, Point cardsLocation, bool down, Tuple<int, int> maxRaise)
    {
        RightCard = rightCard;
        LeftCard = RightCard + 1;
        Name = name;
        Chips = 10000;
        Type = -1;
        Power = 0;
        Turn = false;
        FoldTurn = false;
        CardsAnchor = style;
        PreviousCall = 0;
        EnumCasted = enumCasted;
        CardsLocation = cardsLocation;
        UsernameLabelLocation = down ? new Point(CardsLocation.X, CardsLocation.Y - 20) : new Point(CardsLocation.X, CardsLocation.Y + Settings.Height);
        PanelLocation = new Point(CardsLocation.X - IndentationPanelXy, CardsLocation.Y - IndentationPanelXy);
        MaxRaise[0] = RoundN(maxRaise.Item1, maxRaise.Item2, 1000);
        MaxRaise[1] = RoundN(Chips, maxRaise.Item2, 1000);
    }
    public Bot(int? chips, AnchorStyles style, string name, int rightCard, int enumCasted, Point cardsLocation, bool down, Tuple<int?, int> maxRaise)
    {
        RightCard = rightCard;
        LeftCard = RightCard + 1;
        Name = name;
        Chips = chips;
        Type = -1;
        Power = 0;
        Turn = false;
        FoldTurn = false;
        CardsAnchor = style;
        PreviousCall = 0;
        EnumCasted = enumCasted;
        CardsLocation = cardsLocation;
        UsernameLabelLocation = down ? new Point(CardsLocation.X, CardsLocation.Y - 20) : new Point(CardsLocation.X, CardsLocation.Y + Settings.Height);
        PanelLocation = new Point(CardsLocation.X - IndentationPanelXy, CardsLocation.Y - IndentationPanelXy);
        MaxRaise[0] = RoundN(maxRaise.Item1, maxRaise.Item2, 1000);
        MaxRaise[1] = RoundN(Chips, maxRaise.Item2, 1000);
    }

    public static int RoundN(int? sChips, int n, double roundUpTo)
    {
        double a = Math.Round((double)sChips / n / roundUpTo, 0) * roundUpTo;
        return (int)a;
    }

    public Bot HighCard(Bot user, ref int previous)
    {
        user = Hp(user, 20, 25, ref previous);
        return user;
    }
    public Bot PairHand(Bot user, ref int previous)
    {
        int rCall = Random.Next(13, 19);
        if (user.Power <= 199 && user.Power >= 140)
        {
            user = Ph(user, rCall, 6, ref previous);
        }
        if (user.Power <= 139 && user.Power >= 128)
        {
            user = Ph(user, rCall, 7, ref previous);
        }
        if (user.Power < 128 && user.Power >= 100)
        {
            user = Ph(user, rCall, 9, ref previous);
        }
        return user;
    }
    public Bot TwoPair(Bot user, ref int previous)
    {
        int rCallandRaise = Random.Next(8, 13);
        if (user.Power <= 299 && user.Power >= 246)
        {
            user = Ph(user, rCallandRaise, 4, ref previous);
        }
        if (user.Power <= 244 && user.Power >= 201)
        {
            user = Ph(user, rCallandRaise, 5, ref previous);
        }
        return user;
    }
    public Bot ThreeOfAKind(Bot user, ref int previous)
    {
        int tCall = Random.Next(4, 11);
        user = Smooth(user, tCall, ref previous);
        return user;
    }
    public Bot Straight(Bot user, ref int previous)
    {
        int sCall = Random.Next(3, 8);
        user = Smooth(user, sCall, ref previous);
        return user;
    }
    public Bot Flush(Bot user, ref int previous)
    {
        int fCall = Random.Next(2, 6);
        user = Smooth(user, fCall, ref previous);
        return user;
    }
    public Bot FullHouse(Bot user, ref int previous)
    {
        int fhCall = Random.Next(1, 5);
        user = Smooth(user, fhCall, ref previous);
        return user;
    }
    public Bot FourOfAKind(Bot user, ref int previous)
    {
        int fkCall = Random.Next(1, 4);
        user = Smooth(user, fkCall, ref previous);
        return user;
    }
    public Bot StraightFlush(Bot user, ref int previous)
    {
        int sfCall = Random.Next(1, 3);
        user = Smooth(user, sfCall, ref previous);
        return user;
    }

    public Bot Fold(Bot user)
    {
        MainPoker.Raising = false;
        user.StatusLabel.Text = @"Fold";
        user.ChipsTextBox.Text = @"Chips : " + user.Chips;
        user.Turn = false;
        user.FoldTurn = true;
        user = (Bot)MainPoker.GetStatus(user);
        return user;
    }
    public Bot Check(Bot user, out int previous)
    {
        user.StatusLabel.Text = @"Check";
        user.ChipsTextBox.Text = @"Chips : " + user.Chips;
        user.Turn = false;
        MainPoker.Raising = false;
        previous = 0;
        user = (Bot)MainPoker.GetStatus(user);
        return user;
    }
    public Bot Call(Bot user, ref int previous)
    {
        if (user.Chips > MainPoker.Call)
        {
            MainPoker.Raising = false;
            user.Turn = false;
            user.Chips -= MainPoker.Call;
            user.ChipsTextBox.Text = @"Chips : " + user.Chips;
            user.StatusLabel.Text = @"Call " + (MainPoker.Call + previous);
            PotText = PotText + MainPoker.Call;
            previous = int.Parse(user.StatusLabel.Text.Substring(5));
            user = (Bot)MainPoker.GetStatus(user);
            return user;
        }

        MainPoker.Raising = false;
        user.Turn = false;
        user.StatusLabel.Text = @"Call " + user.Chips;
        if (user.Chips != null) PotText = PotText + (int)user.Chips;
        else
        {
            MessageBox.Show(@"Unexpected error has occured !");
            Application.Exit();
        }
        user.Chips = 0;
        previous = int.Parse(user.StatusLabel.Text.Substring(5));
        user = (Bot)MainPoker.GetStatus(user);
        return user;
    }
    public Bot Raised(Bot user, ref int previous)
    {
        user.Chips -= Convert.ToInt32(MainPoker.Raise - previous);
        user.StatusLabel.Text = @"Raise " + MainPoker.Raise;
        user.ChipsTextBox.Text = @"Chips : " + user.Chips;
        PotText = PotText + Convert.ToInt32(MainPoker.Raise);
        MainPoker.Call = Convert.ToInt32(MainPoker.Raise);
        MainPoker.TempCall = MainPoker.Call;
        MainPoker.Raising = true;
        user.Turn = false;
        previous = int.Parse(user.StatusLabel.Text.Substring(6));
        user = (Bot)MainPoker.GetStatus(user);
        return user;
    }

    public Bot Raise(Bot user, int min, ref int previous)
    {
        int countPassable = 0;
        foreach (var t in MaxRaise)
        {
            if ((MainPoker.Call + previous) * 2 <= t) countPassable++;
        }
        if (countPassable == MaxRaise.Length)
        {
            if ((MainPoker.Call + previous) * 2 == 0)
            {
                if (MainPoker.Bb <= user.Chips)
                {
                    MainPoker.Raise = MainPoker.Bb;
                }
                else
                {
                    user = Check(user, out previous);
                }
            }
            else
            {
                MainPoker.Raise = (MainPoker.Call + previous) * 2;
            }
        }
        user = Raised(user, ref previous);
        return user;
    }

    public Bot Hp(Bot user, int n, int n1, ref int previous)
    {
        int rnd = Random.Next(1, 3);
        int min = RoundN(user.Chips, n, 100);
        int max = RoundN(user.Chips, n1, 100);
        if (MainPoker.Call <= 0)
        {
            user = Check(user, out previous);
        }
        if (MainPoker.Call > 0)
        {
            if (rnd == 1)
            {
                user = MainPoker.Call <= min ? Call(user, ref previous) : Fold(user);
            }
            else
            {
                user = MainPoker.Call <= max ? Call(user, ref previous) : Fold(user);
            }
        }
        if (user.Chips <= 0)
        {
            user.FoldTurn = true;
        }
        return user;
    }

    public Bot Ph(Bot user, int min, int max, ref int previous)
    {
        int rnd = Random.Next(1, 3);
        int _min = RoundN(user.Chips, min, 100);
        int _max = RoundN(user.Chips, max, 100);
        int _min1 = RoundN(user.Chips, min - rnd, 100);
        int _max1 = RoundN(user.Chips, max - rnd, 100);
        if (MainPoker.Rounds < 2)
        {
            user = MainPoker.Call <= 0 ? Check(user, out previous) : Ph_1(user, _min, _max, ref previous);
        }
        else
        {
            user = MainPoker.Call <= 0 ? Raise(user, _min1, ref previous) : Ph_1(user, _min1, _max1, ref previous);
        }
        if (user.Chips <= 0)
        {
            user.FoldTurn = true;
        }
        return user;
    }
    public Bot Ph_1(Bot user, int minimum, int maximum, ref int previous)
    {
        if (MainPoker.Call >= maximum)
        {
            user = Fold(user);
        }
        else
        {
            if (MainPoker.Call >= minimum && MainPoker.Call <= maximum ||
                MainPoker.Call <= minimum && MainPoker.Call >= minimum / 2)
            {
                user = Call(user, ref previous);
            }
            else if (MainPoker.Call <= 0)
            {
                user = Check(user, out previous);
            }
            else
            {
                user = Call(user, ref previous);
            }
        }
        return user;
    }

    public Bot Smooth(Bot user, int n, ref int previous)
    {
        int rnd = Random.Next(1, 3);
        int min = RoundN(user.Chips, n, 100);
        if (MainPoker.Call <= 0)
        {
            if (rnd == 1)
            {
                user = Check(user, out previous);
            }
            else
            {
                Raise(user, min, ref previous);
            }
        }
        else
        {
            if (MainPoker.Call >= min)
            {
                user = Call(user, ref previous);
            }
            else
            {
                Raise(user, MainPoker.Call * 2, ref previous);
            }
        }
        if (user.Chips <= 0)
        {
            user.FoldTurn = true;
        }
        return user;
    }
}
\$\endgroup\$
  • \$\begingroup\$ UsersProperties seems to be a property rather than a base class. \$\endgroup\$ – wonderbell Mar 12 '16 at 22:20
  • \$\begingroup\$ Well i called it base class because 2 classes inherit from it that makes them derived and the class that the derived class inherit i think is called base class or do you mean that it's property because it contains only properties ? \$\endgroup\$ – Denis Mar 12 '16 at 22:24
  • 1
    \$\begingroup\$ It seems to me that you have both game information and display information in your class. Game information should be in one class, display information should be in another class. How you display the game should not be connected with how the game is played. \$\endgroup\$ – pacmaninbw Mar 13 '16 at 1:04
  • \$\begingroup\$ @denis, yes UserProperties seems to contains only game data, so it could be a class member. It could be an inherited property from the base class to Player and Bot. \$\endgroup\$ – wonderbell Mar 13 '16 at 12:57
3
\$\begingroup\$

I do not think it is a good idea to build for a powerful player class for a card game.

If I were you, I'd like to define a minimized abstract Player interface and implement it with bot and humanPlayer if they do vary.

The player interface would contain only cards, score and something for identifying.

How about the game info?

Leave that for a game instance, abstract players considered as 'card holders' would make the design less coupled.

Of course I'd like to provide some sample code if that would help... But I don't quite understand the rule of your card game. So my code may not be useful in your project.

I'll try to make a generic design a card game regardless of its rules in some C# styled pseudo code.

The Player interface.

interface Player
{
  ICollection<Card> HandCards;
  IIdentity Id; 
  ICalculable Score;

  IGameAction Play();
}

I'll explain it: Of course a player should have his hand cards in a collection, his id and his score (if necessary).

So...What's IIdentity and ICalculable? Don't mind, you can replace IIdentity with string, int, long or anything can be used as Id. So it is with ICalculabe, feel free to use int, double, decimal ...

The only method I'm sure with is play. Although there may be other designs, for example, void play(IGame) . I personally prefer returning a game action and let something maybe a game manager to act as a delegate thus we can resist directly changing the game state by a player.

And for IGameAction, it maybe a play, draw, discard ... it depends on you rule.

Maybe you need to distinguish a bot from a human player. So maybe you need:

interface IBot
{
  IStrategyProvider provider;
  IGameAction GetNextStep();
}

It depends on your requirement whether to use a global static StrategyProvider for all bots or get providers of different play style from a provider factory. GetNextStep in fact just fetches the calculation results from providers.

You may feel strange that I didn't mention the game history. That should be hold in a enumerable collection by something like a Game instance or History instance. And a StrategyProvider would refer to the history in its calculation.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Can you provide some sample code ? I'm still beginner and i'm not sure how to correctly implement it. Maybe just with 1 property in the classes that will describe what i am supposed to keep in there \$\endgroup\$ – Denis Mar 13 '16 at 12:23
  • \$\begingroup\$ Also if i need to pass the "Base" class as a parameter and i also need some info from the class that contains the textbox info etc do i need to pass both classes or is there a more compact way to do it ? \$\endgroup\$ – Denis Mar 13 '16 at 12:40
1
\$\begingroup\$

A constructor with more than 10 arguments is clearly a sign that your class is doing way to much. One great opportunity to simplify the code is to separate the user interface from the logic of the game.

You Player or Bot classes should neither be concerned with the position of labels and panels nor with their contents. In addition to making your classes easier to understand, it also makes them easier to test and to reuse in another context.

I suggest to write one method to apply the status of the game to the user interface and to call this method after each round or move regardless of what has changed.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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