Our players have the ability to claim routes and determine which ones they want, but they cannot yet draw cards efficiently. In this post, we will make them have the ability to both draw new destination cards when they need them, and draw new train cards according to their desired colors.

Not THAT kind of drawing! Photo by Neven Krcmarek / Unsplash

The Sample Project

As always with my code-heavy posts, there’s a sample project on GitHub that you can use to follow along with this one. Check it out!

exceptionnotfound/TicketToRideModelingPractice

Drawing Destination Cards

In a real game of Ticket to Ride, you can draw new destination cards at any time.

But drawing new destination cards is most commonly done when at least one of the player’s current destination cards is completed. Newer players will most likely wait until all their current cards are either completed or impossible to finish.

So, our players will draw destination cards under the following circumstances:

  1. All their current destination cards are either complete or impossible to finish AND
  2. They currently have 5 or less destination cards already.

The second condition is to prevent the game from running out of destination cards.

We know from calculating the targeted routes that if no targeted routes are found, then the destination cards the player already has are completed or not completable. So, all we do is instruct our player that, if there are no targeted routes for him/her, they should draw destination cards.

public class Player
{
    //...Properties and Other Methods
    public void TakeTurn()
    {
        CalculateTargetedRoutes();

        //The calculation for Desired Routes only returned routes 
        //that can still be claimed.
        //If there are no desired routes, then all routes 
        //the player wants are already claimed
        //(whether by this player or someone else).
        //Therefore, if there are no desired routes, draw new destination cards
        if (!TargetedRoutes.Any())
        {
            if (!Board.DestinationCards.Any())
            {
                Console.WriteLine("No destination cards remain! " 
                                  + Name + " must do something else!");
            }
            else if (DestinationCards.Count < 5)
            {
                DrawDestinationCards(); //This method is implemented later
                                        //in this post.
                return;
            }
        }
    }
}

But exactly how does the player draw new destination cards?

The rules of Ticket to Ride state that when a player does this, they will get three destination cards. They must keep one of them, but they can keep as many as they like.

For our simulation, we’re going to say that the player will only ever keep one of the three destination cards they are dealt, and will return the other two to the pile. The question is: which one?

Some of the criteria here are obvious. For example, if the player is lucky and draws a destination card they have already completed, obviously they’ll keep that one because it is essentially free points. But in order to check if the route is already connected, we’ll need several new methods in the BoardRouteCollection class.

Modifications to BoardRouteCollection

The first new method we need is very similar to one we have already written: we need a variation of the GetConnectedCities method that only returns cities that are connected by a given player. We also need a new method to check for a direct route between two cities.

public List<CityLength> GetConnectingCitiesForPlayer(City origin, PlayerColor color)
{
    var destinations = Routes.Where(x => x.Origin == origin 
                                         && x.IsOccupied 
                                         && x.OccupyingPlayerColor == color)
                             .Select(x => new CityLength() 
                             { 
                                 City = x.Destination, 
                                 Length = x.Length 
                             })
                             .ToList();

    var origins = Routes.Where(x => x.Destination == origin
                                    && x.IsOccupied 
                                    && x.OccupyingPlayerColor == color)
                        .Select(x => new CityLength() 
                        { 
                            City = x.Origin, 
                            Length = x.Length 
                        })
                        .ToList();

    destinations.AddRange(origins);
    return destinations.Distinct().OrderBy(x => x.Length).ToList();
}

public BoardRoute GetDirectRouteForPlayer(City origin, 
                                          City destination, 
                                          PlayerColor color)
{
    var route = Routes.Where(x => (x.Origin == origin 
                                   && x.Destination == destination 
                                   && x.IsOccupied 
                                   && x.OccupyingPlayerColor == color)
                               || (x.Origin == destination 
                                   && x.Destination == origin 
                                   && x.IsOccupied
                                   && x.OccupyingPlayerColor == color))
                        .FirstOrDefault();

    return route;
}

This pattern should look familiar, as we did something very close to this to find the ideal routes back in Part 3.

We also need a brand new method that uses the new GetConnectingCitiesForPlayer() and GetDirectRouteForPlayer() methods to see if two cities are connected by a given player. We call that method IsAlreadyConnected().

#modeling practice #c# #programming-c #csharp

Modeling Ticket to Ride in C# Part 5: Player Drawing Cards
2.10 GEEK