private RCT2TrackData GenerateWoodenRollerCoasterTrack()
        {
            RCT2TrackData trackData = new RCT2TrackData();

            trackData.TrackData = new List <RCT2TrackPiece>();
            Random random = new Random();

            //Begin Station
            trackData.TrackData.Add(new RideData.RCT2TrackPiece()
            {
                TrackElement = RideData.RCT2TrackElements.RCT2TrackElement.BeginStation,
                Qualifier    = new RideData.RCT2Qualifier()
                {
                    IsChainLift             = false,
                    TrackColourSchemeNumber = 0,
                    TrackRotation           = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero,
                    AtTerminalStation       = false,
                    StationNumber           = 0
                }
            });

            //End Station
            trackData.TrackData.Add(new RideData.RCT2TrackPiece()
            {
                TrackElement = RideData.RCT2TrackElements.RCT2TrackElement.EndStation,
                Qualifier    = new RideData.RCT2Qualifier()
                {
                    IsChainLift             = false,
                    TrackColourSchemeNumber = 0,
                    TrackRotation           = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero,
                    AtTerminalStation       = false,
                    StationNumber           = 0
                }
            });

            //TODO: Marking valid track pieces as invalid
            //      The prior connection check is failing

            for (int i = 2; i < length; i++)
            {
                List <RCT2TrackElements.RCT2TrackElement> candidates = RCT2TrackElements.FindValidSuccessors(whitelistedTracks, trackData.TrackData[i - 1].TrackElement);
                bool reselect = false;

                do
                {
                    //If we have no candidates left
                    if (candidates.Count <= 0)
                    {
                        return(null);
                        //if (steppedBack)
                        //{
                        //    return null;
                        //}

                        //if (i < 3)
                        //{
                        //    throw new Exception("ERROR: Unable to create Coaster - Index stepped back too far");
                        //}
                        ////Console.WriteLine("ERROR: No Valid Track Pieces Found, Stepping back and starting again");
                        //trackData.TrackData.Remove(trackData.TrackData.Last());
                        //i -= 2;
                        //steppedBack = true;


                        //TODO - Make this a better solution, right now it's a hack to make it work
                        //break;
                    }

                    //Select our successor and remove it from the potential pool
                    RCT2TrackElements.RCT2TrackElement successor = candidates[random.Next(candidates.Count)];
                    candidates.Remove(successor);
                    RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[successor];

                    //Console.WriteLine($"\tAttempting Track Piece {successor.ToString()}");

                    RCT2Qualifier qualifier;

                    //Create our qualifier bit
                    if (property.Displacement.Y > 0)
                    {
                        //TODO: Check if it should be a chain
                        //      Perform a lookup to see if previous track was chain lift
                        //      Or if our current velocity would be negative/0
                        //      If not possible, do a lookup to see if previous X tracks contained a decline, if not we need one

                        bool chainLift = trackData.TrackData[i - 1].Qualifier.IsChainLift;

                        //If our current velocity would be negative or zero, we need a chain lift


                        qualifier = new RCT2Qualifier()
                        {
                            IsChainLift             = true,
                            TrackColourSchemeNumber = 0,
                            TrackRotation           = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero,
                            AtTerminalStation       = false,
                            StationNumber           = 0
                        };
                    }
                    else
                    {
                        qualifier = new RCT2Qualifier()
                        {
                            IsChainLift             = false,
                            TrackColourSchemeNumber = 0,
                            TrackRotation           = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero,
                            AtTerminalStation       = false,
                            StationNumber           = 0
                        };
                    }

                    //Add to track
                    trackData.TrackData.Add(new RCT2TrackPiece()
                    {
                        TrackElement = successor, Qualifier = qualifier
                    });

                    //If it's invalid
                    RCT2TrackData.InvalidityCode invalidityCode = trackData.CheckValidity();
                    if (invalidityCode != RCT2TrackData.InvalidityCode.Valid)
                    {
                        //Remove it and try again
                        reselect = true;
                        trackData.TrackData.Remove(trackData.TrackData.Last());
                        //Console.WriteLine($"\tInvalid Selection, Code: {invalidityCode.ToString()}");
                    }
                    else
                    {
                        reselect = false;
                    }
                } while (reselect);

                //Console.WriteLine($"{trackData.TrackData.Last().TrackElement.ToString()} Selected!");
            }

            //for (int i = 0; i < trackData.TrackData.Count(); i++)
            //{
            //    Console.WriteLine("\t" + trackData.TrackData[i].TrackElement.ToString());
            //}

            return(trackData);
        }
        private List <RCT2RideData> Crossover(RCT2RideData parent1, RCT2RideData parent2)
        {
            List <RCT2RideData> children = new List <RCT2RideData>();
            int  crossoverPoint          = length;
            int  redoCount = 0;
            bool redo      = false;

            RCT2TrackData.InvalidityCode parent1Invalidity = parent1.TrackData.CheckValidity();
            RCT2TrackData.InvalidityCode parent2Invalidity = parent2.TrackData.CheckValidity();

            //Create crossover point
            if (random.NextDouble() <= crossoverRate)
            {
                crossoverPoint = random.Next(length);
            }

            do
            {
                if (redoCount >= crossoverAttempts)
                {
                    crossoverPoint = length;
                }

                //Add first halves to each child
                RCT2TrackData child1Track = new RCT2TrackData();
                RCT2TrackData child2Track = new RCT2TrackData();
                for (int i = 0; i < crossoverPoint; i++)
                {
                    child1Track.TrackData.Add(parent1.TrackData.TrackData[i]);
                    child2Track.TrackData.Add(parent2.TrackData.TrackData[i]);
                }

                //Add second halves to each child
                for (int i = crossoverPoint; i < parent2.TrackData.TrackData.Count(); i++)
                {
                    child1Track.TrackData.Add(parent2.TrackData.TrackData[i]);
                }
                for (int i = crossoverPoint; i < parent1.TrackData.TrackData.Count(); i++)
                {
                    child2Track.TrackData.Add(parent1.TrackData.TrackData[i]);
                }

                RCT2RideData child1 = new RCT2RideData(parent1);
                RCT2RideData child2 = new RCT2RideData(parent2);
                child1.TrackData = child1Track;
                child2.TrackData = child2Track;

                //If the created children are invalid, keep trying
                //Wont cause an infinite loop as we will eventually crossover at point 0
                //Which acts as if we never crossed over at all
                RCT2TrackData.InvalidityCode child1Invalidity = child1.TrackData.CheckValidity();
                RCT2TrackData.InvalidityCode child2Invalidity = child2.TrackData.CheckValidity();
                if (child1Invalidity != RCT2TrackData.InvalidityCode.Valid ||
                    child2Invalidity != RCT2TrackData.InvalidityCode.Valid)
                {
                    redo = true;
                    redoCount++;
                    crossoverPoint = random.Next(length);
                }
                else
                {
                    redo = false;
                    children.Add(child1);
                    children.Add(child2);
                    if (crossoverPoint != length)
                    {
                        successfulCrossovers++;
                    }
                }
            } while (redo);

            return(children);
        }