// ferrysim.cpp // ---------------------------------------------------------------------- // // CSE 143 // Homework 4 // http://www.cs.washington.edu/education/courses/143/00su/homework/ // 21 Jul 2000, Ken Yasuhara // 23 Jul 2000, corrected comment in processCarBoarding, line 451 #include #include #include "ferrysim.h" #include "ferrysimview.h" FerrySim::FerrySim(const char carDataFile[], const char ferryDataFile[]) { assert(strlen(carDataFile) > 0 && strlen(ferryDataFile) > 0); // adjust second arg. to change animation speed; // smaller number => slower animation viewPtr = new FerrySimView(this, 0.20); assert(viewPtr != NULL); currentTime = 0; totalTicketRevenue = 0; ferryDepartureCount = 0; dockedFerryPtr = NULL; dockedFerryDepartureTime = -1; carDataStream.open(carDataFile, ios::nocreate); assert(carDataStream); ferryDataStream.open(ferryDataFile, ios::nocreate); assert(ferryDataStream); // output was used for testing before FerrySimView was implemented debugOutput = false; } FerrySim::~FerrySim() { // delete any Car, Ferry instances left in queues if (isFerryDocked()) { delete dockedFerryPtr; dockedFerryPtr = NULL; } while (!ticketingQ.isEmpty()) { delete ticketingQ.dequeue(); } while (!boardingQ.isEmpty()) { delete boardingQ.dequeue(); } } void FerrySim::run() { static int prevEventTime = 0; generateCarArrival(); generateFirstFerryArrival(); viewPtr->paintSnapshot(); while (!eventList.isEmpty()) { if (debugOutput) { cout << "----- " << eventList.getSize() << " events: "; eventList.print(); } FerrySimEvent currEvent = eventList.removeFirst(); currentTime = currEvent.getTime(); if (debugOutput) { char c; cin >> c; cout << "----- processing event at " << currentTime << endl; cout << "ticketing: "; ticketingQ.print(); cout << "boarding: "; boardingQ.print(); } switch (currEvent.getID()) { case FerrySimEvent::CAR_ARRIVAL: processCarArrival(currEvent); break; case FerrySimEvent::CAR_TICKETING: processCarTicketing(currEvent); break; case FerrySimEvent::FERRY_ARRIVAL: processFerryArrival(currEvent); break; case FerrySimEvent::CAR_BOARDING: processCarBoarding(currEvent); break; case FerrySimEvent::FERRY_DEPARTURE: processFerryDeparture(currEvent); break; } // repaint the picture, taking into account how much time elapsed int elapsedTime = currentTime - prevEventTime; // time since last event viewPtr->pause(elapsedTime); // wait... prevEventTime = currentTime; viewPtr->paintSnapshot(); } } int FerrySim::computeFare(const Car *carPtr) const { return FARE_PER_CAR_WITH_DRIVER + FARE_PER_ADDITIONAL_PASSENGER * carPtr->getPassengerCount(); } ////////////////////////////////////////////////////////////////////// // generating, enqueueing FerrySimEvents bool FerrySim::generateFirstFerryArrival() { if (!ferryDataStream) { return false; } // read ferry data from file int arrivalTime, timeBetweenArrivals, carCapacity, personCapacity; ferryDataStream >> arrivalTime >> timeBetweenArrivals >> carCapacity >> personCapacity; ferryDataStream.eatwhite(); // construct ferry and arrival event; put in event list Ferry *newFerryPtr = new Ferry(arrivalTime, timeBetweenArrivals, carCapacity, personCapacity); FerrySimEvent newEvent(arrivalTime, FerrySimEvent::FERRY_ARRIVAL, NULL, newFerryPtr); eventList.insert(newEvent); if (debugOutput) { cout << "first ferry scheduled for " << arrivalTime << endl; } // just one ferry in this version of the ferry sim. ferryDataStream.close(); return true; } bool FerrySim::generateCarArrival() { if (!carDataStream) { return false; } // try to read next car data from file int arrivalTime, passengerCount; if (!(carDataStream >> arrivalTime >> passengerCount)) { // on error, assume end of file carDataStream.close(); return false; } carDataStream.eatwhite(); // validate car data if (arrivalTime < currentTime || passengerCount < 0) { cerr << "invalid car data: " << arrivalTime << " " << passengerCount << endl; return false; } // insert arrival event for this car Car *newCarPtr = new Car(arrivalTime, passengerCount); FerrySimEvent newEvent(arrivalTime, FerrySimEvent::CAR_ARRIVAL, newCarPtr, NULL); eventList.insert(newEvent); if (debugOutput) { cout << "car" << newCarPtr->getInstanceID() << ", arrival scheduled for " << arrivalTime << endl; } return true; } void FerrySim::generateCarTicketing(Car *carPtr) { // at least, this car must be at head of ticketing queue assert(!ticketingQ.isEmpty()); // insert event for completion of ticketing this car FerrySimEvent newEvent(currentTime + CAR_TICKETING_TIME, FerrySimEvent::CAR_TICKETING, carPtr, NULL); eventList.insert(newEvent); if (debugOutput) { cout << "car" << carPtr->getInstanceID() << ", ticketing scheduled to complete at " << currentTime + CAR_TICKETING_TIME << endl; } } bool FerrySim::generateCarBoarding(Car *carPtr) { // For a car to begin boarding, the ferry must be at the dock, // and at least one car, the one that will board, must be in the // boarding queue. assert(isFerryDocked()); assert(!boardingQ.isEmpty()); // check for adequate time to complete another boarding, // that ferry can fit this car if (dockedFerryDepartureTime - currentTime < CAR_BOARDING_TIME || !dockedFerryPtr->canFit(carPtr)) { return false; } // insert event for completion of boarding this car FerrySimEvent boarding(currentTime + CAR_BOARDING_TIME, FerrySimEvent::CAR_BOARDING, carPtr, dockedFerryPtr); eventList.insert(boarding); if (debugOutput) { cout << "car" << carPtr->getInstanceID() << ", boarding scheduled to complete at " << currentTime + CAR_BOARDING_TIME << endl; } return true; } void FerrySim::generateFerryDeparture() { // ferry can't depart unless it's at the dock now assert(isFerryDocked()); assert(dockedFerryDepartureTime > currentTime); // schedule departure; add to event list FerrySimEvent departure(dockedFerryDepartureTime, FerrySimEvent::FERRY_DEPARTURE, NULL, dockedFerryPtr); eventList.insert(departure); if (debugOutput) { cout << "ferry departure scheduled for " << dockedFerryDepartureTime << endl; } } void FerrySim::generateNextFerryArrival() { // next ferry arrival is based on currently docked ferry assert(isFerryDocked()); // next ferry is just like current one, except arrival time differs Ferry *nextFerryPtr = new Ferry(dockedFerryPtr->getArrivalTime() + dockedFerryPtr->getTimeBetweenArrivals(), dockedFerryPtr->getTimeBetweenArrivals(), dockedFerryPtr->getCarCapacity(), dockedFerryPtr->getPersonCapacity()); // schedule next arrival; add to event list FerrySimEvent nextArrival(currentTime + dockedFerryPtr->getTimeBetweenArrivals(), FerrySimEvent::FERRY_ARRIVAL, NULL /* no car */, nextFerryPtr); eventList.insert(nextArrival); if (debugOutput) { cout << "next ferry arrival scheduled for " << currentTime + dockedFerryPtr->getTimeBetweenArrivals() << endl; } } ////////////////////////////////////////////////////////////////////// // processing FerrySimEvents void FerrySim::processCarArrival(const FerrySimEvent &event) { assert(event.getID() == FerrySimEvent::CAR_ARRIVAL); Car *carPtr = event.getCarPtr(); assert(carPtr->getArrivalTime() == currentTime); if (debugOutput) { cout << "car" << carPtr->getInstanceID() << " arrived, entering ticketing queue" << endl; } // Upon arrival, cars start in the ticketing queue. ticketingQ.enqueue(carPtr); // If no other cars were awaiting ticketing, // begin ticketing this car. if (ticketingQ.getSize() == 1) { if (debugOutput) { cout << "only this car awaiting ticketing" << endl; } generateCarTicketing(carPtr); } // If more cars are arriving in the future, schedule the // arrival of the next one. generateCarArrival(); } void FerrySim::processCarTicketing(const FerrySimEvent &event) { assert(event.getID() == FerrySimEvent::CAR_TICKETING); // The car in this event should not be ticketed yet and should be // at the head of the ticketing queue. Car *carPtr = event.getCarPtr(); assert(!carPtr->isTicketed()); assert(ticketingQ.getItemAt(0) == carPtr); if (debugOutput) { cout << "car" << carPtr->getInstanceID() << " ticketed, entering boarding queue" << endl; } // Ticket this car and put it in the queue of cars waiting to board. totalTicketRevenue += computeFare(carPtr); carPtr->issueTicket(); //ticketedCarCount++; // Move this car from the ticketing queue to the boarding queue. Car *dequeuedCarPtr = ticketingQ.dequeue(); assert(dequeuedCarPtr == carPtr); boardingQ.enqueue(carPtr); // If there's already a ferry docked and no one was waiting to // board, begin boarding the car we just enqueued. if (boardingQ.getSize() == 1 && isFerryDocked()) { cout << "only this car awaiting boarding" << endl; generateCarBoarding(carPtr); } // If there are other cars awaiting ticketing... if (!ticketingQ.isEmpty()) { if (debugOutput) { cout << "starting to ticket next car, car" << ticketingQ.getItemAt(0)->getInstanceID() << "..." << endl; } // ...begin ticketing the next one. generateCarTicketing(ticketingQ.getItemAt(0)); } } void FerrySim::processFerryArrival(const FerrySimEvent &event) { assert(event.getID() == FerrySimEvent::FERRY_ARRIVAL); assert(!isFerryDocked()); dockedFerryPtr = event.getFerryPtr(); // Newly arrived ferries should be able to fit at least one car. assert(dockedFerryPtr->getCarCapacity() > 0); if (debugOutput) { cout << "ferry arrived at " << getCurrentTime() << endl; } // Schedule this ferry's departure. Time spent at dock is exactly the // time required to board as many cars as this ferry can fit, unless this // time exceeds the time between consecutive ferries. int dockingTime = dockedFerryPtr->getCarCapacity() * CAR_BOARDING_TIME + 1; if (dockingTime > dockedFerryPtr->getTimeBetweenArrivals()) { dockingTime = dockedFerryPtr->getTimeBetweenArrivals() - 1; } dockedFerryDepartureTime = currentTime + dockingTime; generateFerryDeparture(); // Schedule next ferry arrival. generateNextFerryArrival(); // If cars were awaiting boarding, begin boarding the first one. if (!boardingQ.isEmpty()) { if (debugOutput) { cout << "cars were waiting for ferry; beginning boarding" << endl; } generateCarBoarding(boardingQ.getItemAt(0)); } } void FerrySim::processCarBoarding(const FerrySimEvent &event) { assert(event.getID() == FerrySimEvent::CAR_BOARDING); assert(isFerryDocked()); // The car in this event should be ticketed but not boarded yet // and should be at the head of the boarding queue. Car *carPtr = event.getCarPtr(); assert(carPtr->isTicketed() && !carPtr->isBoarded()); assert(boardingQ.getItemAt(0) == carPtr); // Board this car. carPtr->board(); dockedFerryPtr->board(carPtr); if (debugOutput) { cout << "car" << carPtr->getInstanceID() << " boarded" << endl; } // Remove this car from the boarding queue. Car *dequeuedCarPtr = boardingQ.dequeue(); assert(dequeuedCarPtr == carPtr); // no longer need this Car object delete carPtr; // If there are other cars awaiting boarding... if (!boardingQ.isEmpty()) { if (debugOutput) { cout << "starting to board next car..." << endl; } // ...attempt to begin boarding the next one. generateCarBoarding(boardingQ.getItemAt(0)); } } void FerrySim::processFerryDeparture(const FerrySimEvent &event) { assert(event.getID() == FerrySimEvent::FERRY_DEPARTURE); assert(isFerryDocked()); delete dockedFerryPtr; dockedFerryPtr = NULL; dockedFerryDepartureTime = -1; ferryDepartureCount++; cout << "ferry departed" << endl; }