public static int Run(bool isRanByUser, string[] args) { int target = -1; target = !isRanByUser?int.Parse(args[0]) : GetParametersFromConsole(); if (target > 8 || target < 0) { throw new ArgumentException("We're not ready to handle values over 8 or below 0 yet"); } List <StateOfBottles> listOfStates = new List <StateOfBottles>(); //In order to perform a breadth-first search we must populate this list sorted by actions taken. StateOfBottles state = new StateOfBottles(); listOfStates.Add(state); int variant = 0; while (true) { if (state.EvaluateIfTargetIsReached(target)) { break; } state = listOfStates.First(); listOfStates.Remove(state); //Only used for manual debugging. //Console.WriteLine($"Itteration: {variant}, Five: {state.FiveLiterBottle.CurrentVolume}, Three: {state.ThreeLiterBottle.CurrentVolume}, Target: {target}, ActionTaken: {state.ActionTaken}"); listOfStates = StateOfBottles.AddAnotherRound(listOfStates, state); variant += 1; } Console.WriteLine($"To find {target} requires {state.ActionsTaken} steps, we evaluated {variant} different states in order to find this path."); Console.WriteLine("Press Enter to display the actions taken"); if (isRanByUser) { Console.ReadLine(); } Console.WriteLine($"Actions Taken to reach the route:"); var statesToAccomplishTask = CalculateStepsTaken(new List <ValidActions>(), state); foreach (var x in statesToAccomplishTask) { Console.WriteLine((object)x); } if (isRanByUser) { Console.ReadLine(); } return(state.ActionsTaken); }
/// <summary> /// Be careful using this constructor, it's intended to be used internally. /// </summary> /// <param name="parentState"></param> /// <param name="actionToTake"></param> private StateOfBottles(StateOfBottles parentState, ValidActions actionToTake) { //TODO: Verify if simply FiveLiterBottle = ParentState.FiveLiterBottle is a copy or a reference //For now we play safe and copy manually FiveLiterBottle = new Bottle(parentState.FiveLiterBottle); ThreeLiterBottle = new Bottle(parentState.ThreeLiterBottle); this.ParentState = parentState; ActionTaken = actionToTake; ActionsTaken = parentState.ActionsTaken + 1; switch (actionToTake) { case ValidActions.Fill5: FiveLiterBottle.Fill(); break; case ValidActions.Fill3: ThreeLiterBottle.Fill(); break; case ValidActions.Empty5: FiveLiterBottle.Empty(); break; case ValidActions.Empty3: ThreeLiterBottle.Empty(); break; case ValidActions.Pour5To3: ThreeLiterBottle.AddToVolumeFromThisBottle(FiveLiterBottle); break; case ValidActions.Pour3To5: FiveLiterBottle.AddToVolumeFromThisBottle(ThreeLiterBottle); break; default: throw new ArgumentOutOfRangeException(nameof(actionToTake), actionToTake, null); } }
/// <summary> /// Adds all actions used in ValidActions to the end of the provided list. /// </summary> /// <param name="list"></param> /// <param name="state"></param> /// <returns>List, with all actions from ValidActions applied to the end, using the supplied state as first action.</returns> public static List <StateOfBottles> AddAnotherRound(List <StateOfBottles> list, StateOfBottles state) { var variant = Enum.GetValues(typeof(ValidActions)).Cast <ValidActions>(); foreach (var action in variant) { //Console.WriteLine($"Performing action: {action}"); list.Add(state.Perform(action)); } return(list); }
private static List <ValidActions> CalculateStepsTaken(List <ValidActions> list, StateOfBottles state) { if (state == null) { list.Reverse(); //Reverse the list in order to output the actions in correct order (the state without a parent is the first action we take). return(list); } if (state.ActionTaken.HasValue) { list.Add(state.ActionTaken.Value); } return(CalculateStepsTaken(list, state.ParentState)); }