public LaneSchedulerAction(int leftmostLane, LaneSchedulerReservation reservation, double weight) { this.leftmostLane = leftmostLane; this.lowestTimeSlot = reservation.StartTimeSlot; this.reservation = reservation; this.weight = weight; this.numLanes = reservation.NumberOfLanes; this.numTimeSlots = reservation.NumberOfTimeSlots; }
public static List<LaneSchedulerReservation> GetAlternativeReservations(LaneSchedulerState state, LaneSchedulerReservation reservation) { List<LaneSchedulerReservation> reservations = new List<LaneSchedulerReservation>(); if (reservation.StartTimeSlot - 1 > 0) { LaneSchedulerReservation altReservation1 = new LaneSchedulerReservation(reservation.Id, reservation.NumberOfLanes, reservation.NumberOfTimeSlots, reservation.StartTimeSlot - 1); List<LaneSchedulerAction> actions = LaneScheduler.Expand(state, altReservation1); if (actions.Count > 0) { reservations.Add(altReservation1); } int numTimeSlots = reservation.NumberOfTimeSlots - 1; while (numTimeSlots > 0) { LaneSchedulerReservation altReservation2 = new LaneSchedulerReservation(reservation.Id, reservation.NumberOfLanes, numTimeSlots, reservation.StartTimeSlot - 1); List<LaneSchedulerAction> actions2 = LaneScheduler.Expand(state, altReservation2); if (actions2.Count > 0) { reservations.Add(altReservation2); } numTimeSlots--; } } if (reservation.StartTimeSlot + 1 < state.numberOfTimeSlots) { LaneSchedulerReservation altReservation1 = new LaneSchedulerReservation(reservation.Id, reservation.NumberOfLanes, reservation.NumberOfTimeSlots, reservation.StartTimeSlot + 1); List<LaneSchedulerAction> actions = LaneScheduler.Expand(state, altReservation1); if (actions.Count > 0) { reservations.Add(altReservation1); } int numTimeSlots = reservation.NumberOfTimeSlots - 1; while (numTimeSlots > 0) { LaneSchedulerReservation altReservation2 = new LaneSchedulerReservation(reservation.Id, reservation.NumberOfLanes, numTimeSlots, reservation.StartTimeSlot + 1); List<LaneSchedulerAction> actions2 = LaneScheduler.Expand(state, altReservation2); if (actions2.Count > 0) { reservations.Add(altReservation2); } numTimeSlots--; } } return reservations; }
public static List<LaneSchedulerAction> Expand(LaneSchedulerState state, LaneSchedulerReservation reservation) { List<LaneSchedulerAction> actions = new List<LaneSchedulerAction>(); if (state.IsPossible(reservation)) { for (int lane = 0; lane < state.numberOfLanes; lane++) { AppWeightPair appWeightPair = state.IsApplicable(lane, reservation.NumberOfLanes, reservation.NumberOfTimeSlots, reservation.StartTimeSlot); if (appWeightPair.applicable) { actions.Add(new LaneSchedulerAction(lane, reservation, appWeightPair.weight)); } } } return actions; }
public static bool Test_n_reservations(int numberOfLanes, int numberOfTimeSlots, int numberOfVisitors, int runLimit) { Debug.WriteLine("Testing scheduling of " + numberOfVisitors + " visitors in " + numberOfLanes + " lanes and " + numberOfTimeSlots + " timeslots"); LaneWearData.Populate(numberOfLanes); List<LaneSchedulerReservation> reservations = new List<LaneSchedulerReservation>(); // Reservation(int id, int numLanes, int numTimeSlots, int startTimeSlot) bool run = true; //State emptyState = new State(numberOfLanes, numberOfTimeSlots, reservations); LaneSchedulerState state = new LaneSchedulerState(numberOfLanes, numberOfTimeSlots, reservations); LaneSchedulerState newState = null; int i = 0; int visitors = 0; int runs = 0; long timeSpent = 0; Random random = new Random(); while (run) { int numVisitors = 4; int numTimeSlots = random.Next(1, 3); int startTimeSlot = 6; // random.Next(0, numberOfTimeSlots); if (random.Next(0, 100) < 10) { // 10 percent will be parties and outings numVisitors = random.Next(5, 41); } else { numVisitors = random.Next(3, 7); } if (random.Next(0, 100) < 15) { startTimeSlot = random.Next(0, numberOfTimeSlots); } else { startTimeSlot = random.Next(2, numberOfTimeSlots - 2); } int numLanes = 0; if (numVisitors % 6 == 0) { numLanes = numVisitors / 6; } else { numLanes = (numVisitors / 6) + 1; } long time1 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; int id = i + 1; LaneSchedulerReservation reservation = new LaneSchedulerReservation(id, numLanes, numTimeSlots, startTimeSlot); Debug.WriteLine("Making reservation id: " + id + " of " + numLanes + " lanes for " + numTimeSlots + " hours, at timeslot " + startTimeSlot); List<LaneSchedulerReservation> newReservations = new List<LaneSchedulerReservation>(reservations); newReservations.Add(reservation); //emptyState = new State(numberOfLanes, numberOfTimeSlots, newReservations); LaneSchedulerStateReservationsPair result = LaneScheduler.Search(state, reservations, reservation); newState = result.state; long time2 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; timeSpent = time2 - time1; Debug.WriteLine(" Scheduling took: " + timeSpent + " miliseconds"); if (newState != null) { Debug.WriteLine(" It SUCCEEDED!!!!!!!!"); Debug.WriteLine(newState.ToString()); reservations = newReservations; state = newState; visitors = visitors + numVisitors; Debug.WriteLine("Have now scheduled for: " + visitors + " visitors"); i++; } else { Debug.WriteLine(" It failed. Alternative reservations are:"); for (int j = 0; j < result.reservations.Count; j++) { Debug.WriteLine(" Reservation for " + result.reservations[j].NumberOfLanes + " lanes at slot: " + result.reservations[j].StartTimeSlot + " for " + result.reservations[j].NumberOfTimeSlots + " hours"); } } if (visitors > numberOfVisitors) { run = false; } if (runs > runLimit) { run = false; } runs++; } Debug.WriteLine("The last scheduling took: " + timeSpent + " miliseconds"); if (state != null) { Debug.WriteLine(state.ToString()); } return true; }
public static LaneSchedulerStateReservationsPair Search(LaneSchedulerState state, List<LaneSchedulerReservation> reservations, LaneSchedulerReservation newReservation) { LaneScheduler.closedStateList = new Dictionary<string, int>(); Debug.WriteLine("Adding new Reservation"); if (!state.IsPossible(newReservation)) { Debug.WriteLine(" It failed the first check"); // get other reservations return new LaneSchedulerStateReservationsPair(null, LaneScheduler.GetAlternativeReservations(state, newReservation)); } // if it can be applied easily List<LaneSchedulerAction> actions = Expand(state, newReservation); actions = (from y in actions select y).OrderBy(y => y.weight).ToList<LaneSchedulerAction>(); if (actions.Count > 0) { state.Apply(actions[0]); Debug.WriteLine(" The reservation was straight forward"); return new LaneSchedulerStateReservationsPair(state, new List<LaneSchedulerReservation>()); } // else, search // cut the relevant piece out of the state, so we end up with three pieces. // Solve the middle piece, and add it to the top and bottom piece. //List<State> statePieces = state.cutInPieces(newReservation); List<LaneSchedulerReservation> newReservations = new List<LaneSchedulerReservation>(reservations); newReservations.Add(newReservation); long time1 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; LaneSchedulerState emptyState = new LaneSchedulerState(state.numberOfLanes, state.numberOfTimeSlots, newReservations); LaneSchedulerState newState = LaneScheduler.RecursiveSearch(emptyState, newReservations, 0, 100000, 0); long time2 = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; long runTime = time2 - time1; Debug.WriteLine(" Search took: " + runTime + " ms"); if (newState != null) { Debug.WriteLine(" Returning state"); return new LaneSchedulerStateReservationsPair(newState, new List<LaneSchedulerReservation>()); } else { // get other reservations Debug.WriteLine(" Returning other reservations"); return new LaneSchedulerStateReservationsPair(null, LaneScheduler.GetAlternativeReservations(state, newReservation)); } }
public List<LaneSchedulerState> CutInPieces(LaneSchedulerReservation reservation) { List<LaneSchedulerState> stateList = new List<LaneSchedulerState>(); // detect cutting lines // find cutting line for upper part int upperCut = -1; for (int i = reservation.StartTimeSlot + reservation.NumberOfTimeSlots; i < this.numberOfTimeSlots; i++) { bool foundAny = false; for (int j = 0; j < this.numberOfLanes; j++) { if (this.state[i, j] == this.state[i + 1, j] || this.state[i, j] == this.state[i - 1, j]) { foundAny = true; } if (!foundAny) { upperCut = i; break; } } } // find cutting line for lower part int lowerCut = -1; for (int i = reservation.StartTimeSlot - 1; i >= 0; i--) { bool foundAny = false; for (int j = 0; j < this.numberOfLanes; j++) { if (this.state[i, j] == this.state[i + 1, j] || this.state[i, j] == this.state[i - 1, j]) { foundAny = true; } if (!foundAny) { lowerCut = i; break; } } } // do the cutting LaneSchedulerState stateUpper = new LaneSchedulerState(this.numberOfLanes, this.numberOfTimeSlots, new List<LaneSchedulerReservation>()); stateUpper.weight = this.weight; stateUpper.weight = this.weight; stateUpper.state = new int[numberOfTimeSlots, numberOfLanes]; LaneSchedulerState stateMiddle = new LaneSchedulerState(this.numberOfLanes, this.numberOfTimeSlots, new List<LaneSchedulerReservation>()); stateMiddle.weight = this.weight; stateMiddle.weight = this.weight; stateMiddle.state = new int[numberOfTimeSlots, numberOfLanes]; LaneSchedulerState stateLower = new LaneSchedulerState(this.numberOfLanes, this.numberOfTimeSlots, new List<LaneSchedulerReservation>()); stateLower.weight = this.weight; stateLower.weight = this.weight; stateLower.state = new int[numberOfTimeSlots, numberOfLanes]; // Populate them for (int i = 0; i < this.numberOfTimeSlots; i++) { for (int j = 0; j < this.numberOfLanes; j++) { if (i <= lowerCut) { stateLower.state[i, j] = this.state[i, j]; } else if (j >= upperCut) { stateMiddle.state[i, j] = this.state[i, j]; } else { stateUpper.state[i, j] = this.state[i, j]; } } } stateList.Add(stateUpper); stateList.Add(stateMiddle); stateList.Add(stateLower); return stateList; }
public String ReservationRepr(LaneSchedulerReservation reservation) { StringBuilder builder = new StringBuilder(); builder.Append(reservation.Id); builder.Append(","); builder.Append(reservation.NumberOfLanes); builder.Append(","); builder.Append(reservation.NumberOfTimeSlots); builder.Append(","); builder.Append(reservation.StartTimeSlot); builder.Append("->"); for (int i = reservation.StartTimeSlot - reservation.NumberOfTimeSlots; i < reservation.StartTimeSlot + reservation.NumberOfTimeSlots; i++) { if (i < this.numberOfTimeSlots && i >= 0) { for (int j = 0; j < this.numberOfLanes; j++) { builder.Append(this.state[i, j]); builder.Append("."); } builder.Append("/"); } } return builder.ToString(); }
public bool IsPossible(LaneSchedulerReservation reservation) { for (int i = reservation.StartTimeSlot; i < reservation.NumberOfTimeSlots + reservation.StartTimeSlot; i++) { if (i >= this.numberOfTimeSlots) { return false; } int numFreeCells = 0; for (int j = 0; j < this.numberOfLanes; j++) { if (this.state[i, j] == 0) { numFreeCells++; } } if (numFreeCells < reservation.NumberOfLanes) { return false; } } return true; }
public double GetReservationWeight(LaneSchedulerReservation reservation) { double weight = 0.0f; for (int i = reservation.StartTimeSlot; i < reservation.StartTimeSlot + reservation.NumberOfTimeSlots; i++) { weight = Math.Max(weight, this.GetCellWeight(i)); } weight += 1 / reservation.NumberOfLanes; return weight; }
/// <summary> /// Determines whether the new reservation <paramref name="reservation"/> is possible /// based on the current reservation schedule for the day of that reservations /// </summary> /// <remarks> /// The lane scheduler will be queried to check for availablility. While doing this, /// all current reservations for that day might be rescheduled, therefore, the new schedule /// is returned. This schedule will have to be persisted if the new reservation is acuallly /// added to the database. /// /// The out paramter "newReservation" will contain an reservation instance ready for persisting /// if this is sought. /// /// Execptions will be thrown if the reservation does not fit in the current schedule /// </remarks> /// <param name="reservation">The new reservation</param> /// <param name="theReservation">An intance of a reservation, based on the data from <paramref name="reservation"/></param> /// <returns>The list of rescheduled reservations and an instance ready to commit in <paramref name="theReservation"/></returns> /// <exception cref="ArgumentException">If the <paramref name="reservation"/> is not possible</exception> public List<Reservation> Go(ReservationType reservation, out Reservation theReservation, out bool isPossible, out List<LaneSchedulerReservation> suggestions) { // quick pruning of the result - does the requested timeslot exist at all?? TimeSlot startTimeSlot = timeSlotRepos.GetAll().FindTimeSlotStartingAt(reservation.TimeOfDay); if (startTimeSlot == null) { throw new ArgumentException("Reservation is not possible as the timeslot does not exist", "timeSlot"); } var reservations = reservationRepos.GetAll().FindReservationsByDate(reservation.PlayAt); // convert these into a format the scheduler understands... List<LaneSchedulerReservation> schedulerReservations; var convertToState = ServiceLocator.Current.GetInstance<ReservationsToLaneSchedulerState>(); var schedulerState = convertToState.Convert(reservations.ToList(), out schedulerReservations); LaneSchedulerReservation newReservation = new LaneSchedulerReservation() { Id = -1, NumberOfLanes = (int)Math.Ceiling(reservation.NumberOfPlayers / 6.0m), NumberOfTimeSlots = reservation.HowManyHours, StartTimeSlot = startTimeSlot.Id - 1 }; // do the scheduling var newState = LaneScheduler.Search(schedulerState, schedulerReservations, newReservation); if (newState.state == null) { suggestions = newState.reservations; isPossible = false; theReservation = null; return null; } suggestions = null; // The scheduler attempts to place the reservation +/- one time slot in the schedule if it's full, // so we have to try and handle that var convertToReservation = ServiceLocator.Current.GetInstance<LaneSchedulerStateToReservations>(); var rescheduledReservations = convertToReservation.Convert(newState.state, out theReservation); theReservation.NumberOfPlayers = reservation.NumberOfPlayers; theReservation.PlayAt = reservation.PlayAt; theReservation.Name = reservation.Name; theReservation.PhoneNumber = reservation.PhoneNumber; theReservation.Status = ReservationStatus.Pending; theReservation.CreatedAt = DateTime.Now; // now that we have the actual reservation, we can check // the added time-slots against the requested timeslot if (theReservation.TimeSlots[0].Start != startTimeSlot.Start) { isPossible = false; } else { isPossible = true; } return rescheduledReservations; }