Write a simulation program to simulate lines at a grocery store for a typical da
ID: 3605883 • Letter: W
Question
Write a simulation program to simulate lines at a grocery store for a typical day’s business. You will assume there are three cashier lines at the grocery store. Customers will enter randomly to check out (this is a time stamp in the input file) and will enter the shortest line. If the lines are equal, then the first line in the queue will be selected. Each transaction takes a random amount of time to complete (this also is a number from the input file).
The grocery store operates on a ten hour business day and only 6 customers per queue will be able to wait for service at any time. If a customer arrives and there are 6 in all the queues, the customer will be turned away.
To perform the simulation, a number, a time stamp, and a service time (randomly generated between 1 and 20 minutes) is given for each customer. This information will be in an input file. The first item in the input file will be the number of customers in the file. To keep the simulation simple, this time stamp will be a count of the number of minutes elapsed from the start of the business day. The time stamps will be in order in the file. So the time could be any values from 0 to 570 (no new business will be allowed 30 minutes before closing, so the waiting queue can be cleared out, hopefully, before the end of the business day.) There will be a time counter which will start at 0 when the simulation starts. No two customers can arrive at the same minute. Customers should be taken out of the queue when the cashier starts serving them.
This simulation will inform the owner how many customers were serviced, how many were turned away, the average time any customer in the queue spent waiting, and the total time customers spent waiting.
You will generate your own input file to test your program that will have a number, a time stamp from 0 to 570 in order, and a service time. Those will be your customers. The grader will use a standard file to test all the projects.
To simulate the time, you will use a loop that increments the “timer” by 1 each time through the loop.
Sample data:
1 10 1
2 17 3
3 20 7
4 22 2
5 25 5
.
.
.
30 73 2
Sample output data:
Total number of customers serviced: 35
Total number of customers turned away: 1
Average wait time: 6
Total wait time: 12
Your file names will be makefile, main.cpp, node.hpp, node.cpp, queue.hpp, queue.cpp, and any other .hpp and .cpp files that you need
Explanation / Answer
main.cpp
------------------------------------------------------------
#include "serviceSimulator.hpp"
#include "ServiceSimulator.hpp"
#include <string>
#include <iostream>
using namespace std;
int main()
{
ServiceParms parameters;
// The number of cashiers to be stationed
parameters.numOfCashiers = 3;
// The maximum number of customers per lane
parameters.lineMax = 6;
// The total minutes in a single day
parameters.minInDay = 570;
// True to read customers from file or false to generate daily customers
parameters.readInput = false;
// The minimum and maximum arrival time in minutes
parameters.arrTmMin = 1;
parameters.arrTmMax = 5;
// The minimum and maximum service time in minutes
parameters.serTmMin = 1;
parameters.serTmMax = 20;
parameters.displayLogs = false;
ServiceSimulator sim(parameters);
sim.simulate();
return 0;
}
-------------------------------------------------------------------------------------------------------------------------
serviceSimulator.cpp
--------------------------------------------------------
#include "serviceSimulator.hpp"
#include "queue.hpp"
#include "cashier.hpp"
#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <sstream>
#include <ctime>
using namespace std;
ServiceSimulator::ServiceSimulator(ServiceParms parms)
{
mParms = parms;
mCurrentTime = 1;
mNumberServiced = 0;
mTurnedAway = 0;
mTotWaitTime = 0;
}
// Carries out the service simulation based on the entered parameters.
void ServiceSimulator::simulate()
{
// Creates a number of cashiers equal to the entered parameter
Cashier cashiers[mParms.numOfCashiers] = Cashier(mParms.lineMax);
// for ( int i = 0; i < mParms.numOfCashiers; i++)
// {
// cashiers[i].setMaxInLine(mParms.lineMax);
// }
// Creates the lanes as queues for each customer
Queue lane[mParms.numOfCashiers];
// Determines whether to read from file or generate file
Queue dailyCustomers;
readInputCust(&dailyCustomers);
logMsg("Daily customers generated");
// Next customer to enter store
Customer currentCust = dailyCustomers.GetFront();
bool allLinesFull = true;
// Loop until day is completed + the max service time to account for any
// late customers still in line after store closes
while ( mCurrentTime < mParms.minInDay + (mParms.serTmMax * mParms.lineMax))
{
logMsg("Time of day", mCurrentTime);
// Checks to see if it is the current time that the customer enters
if ( currentCust.getTimeStamp() == mCurrentTime )
{
logMsg("Entered store", mCurrentTime,
dailyCustomers.GetFront().getCustNum(), 1);
// Sets line count to greatest amount or comparison
int shortestLineCount = mParms.lineMax;
int shortestLine;
int currentLineCount;
// Checks to see if all lines are full and updates shortest line
for ( int i = 0; i < mParms.numOfCashiers; i++ )
{
// Lines full == true unless one has space
if ( !cashiers[i].isFull() )
allLinesFull = false;
// Updates shortestLine to the lane with the least customers
currentLineCount = cashiers[i].getCustInLine();
if ( currentLineCount < shortestLineCount )
{
shortestLine = i;
shortestLineCount = currentLineCount;
}
}
// If lane is full, turn away customer, otherwise add it to the
// shortest lane
if ( allLinesFull )
{
logMsg("Turned Away", mCurrentTime,
dailyCustomers.GetFront().getCustNum(), mTurnedAway + 1);
dailyCustomers.Dequeue();
mTurnedAway++;
}
else
{
lane[shortestLine].Enqueue( dailyCustomers.GetFront() );
cashiers[shortestLine].increaseCustInLine();
logMsg("Entered lane", mCurrentTime,
lane[shortestLine].GetRear().getCustNum(), shortestLine);
logMsg("Entered lane count", mCurrentTime,
lane[shortestLine].GetRear().getCustNum(),
lane[shortestLine].getCount());
}
dailyCustomers.Dequeue();
// Only process if there are more customers for the day
if ( !dailyCustomers.isEmpty() )
{
currentCust = dailyCustomers.GetFront();
}
// Resets the marker for all lines full
allLinesFull = true;
}
// Process the customers
processCustomer(lane, cashiers);
incrementTime();
}
logMsg("Processing Finished", mCurrentTime, -1);
OutputResults();
}
// Generates a list of all customers to enter in a single day using the
Queue ServiceSimulator::PopulateCustomers()
{
Queue dailyCustomers;
int custNum = 1;
srand(time(0));
// Randomly generates a time of arrival and time taken to service for each
// customer
int timeStamp = TimeGenerator(mParms.arrTmMin, mParms.arrTmMax);
int serviceTime = TimeGenerator(mParms.serTmMin, mParms.serTmMax);
// Opens Text file and saves each part until inventory is complete.
string fileName = "customers.txt";
ofstream saveFile(fileName.c_str());
// While the end of the day hasn't been reached
do {
Customer cust(custNum, timeStamp, serviceTime);
// Put the customer into the queue
dailyCustomers.Enqueue(cust);
// Generates a customers file with each customer's number, time stamp
// and service time.
if ( !mParms.readInput )
{
if (saveFile.is_open())
{
saveFile << custNum << " " <<
timeStamp << " " <<
serviceTime << endl;
}
}
// Generate new values for the arrival and service time
timeStamp += TimeGenerator(mParms.arrTmMin, mParms.arrTmMax);
serviceTime = TimeGenerator(mParms.serTmMin, mParms.serTmMax);
custNum++;
} while ( timeStamp <= mParms.minInDay);
saveFile.close();
return dailyCustomers;
}
// Read input from a file or generate a queue of daily customers
void ServiceSimulator::readInputCust(Queue* dailyCustomers) {
// Determines whether to read from file or generate file
if (mParms.readInput)
{
string fileName = "customers.txt";
ifstream readFile(fileName.c_str());
string fileLine;
if (readFile.is_open())
{
while (getline(readFile, fileLine))
{
// First value of substring
int sub1 = 0;
// Second value of substring
int sub2 = fileLine.find(' ');
string custNum;
string timeStamp;
string serviceTime;
int custNumVal;
int timeStampVal;
int serviceTimeVal;
// customer number
custNum = fileLine.substr(sub1, (sub2 - sub1));
// Removes the customer number from the line
sub1 = custNum.length() + 1;
sub2 = fileLine.length();
fileLine = fileLine.substr(sub1, (sub2 - sub1));
// Parses the time stamp
sub1 = 0;
sub2 = fileLine.find(' ');
timeStamp = fileLine.substr(sub1, (sub2 - sub1));
// Parses the service time
sub1 = timeStamp.length() + 1;
sub2 = fileLine.length();
serviceTime = fileLine.substr(sub1, (sub2 - sub1));
// Converts time stamp and service time to integers
stringstream convert1;
convert1 << custNum;
convert1 >> custNumVal;
stringstream convert2;
convert2 << timeStamp;
convert2 >> timeStampVal;
stringstream convert3;
convert3 << serviceTime;
convert3 >> serviceTimeVal;
// Creates temp customer and adds it to the queue
Customer temp(custNumVal, timeStampVal, serviceTimeVal);
dailyCustomers->Enqueue(temp);
}
}
readFile.close();
} else {
// Generates a queue of customers to enter store
*dailyCustomers = PopulateCustomers();
}
}
// Allows the cashier to process the customers from the lane and records the
void ServiceSimulator::processCustomer(Queue lane[], Cashier cashiers[])
{
for (int i = 0; i < mParms.numOfCashiers; i++)
{
// If cashier is available and lane has at least one customer
if ( cashiers[i].isAvailable() && !lane[i].isEmpty())
{
// Display log info
logMsg("Wait time", mCurrentTime,
lane[i].GetFront().getCustNum(),
mCurrentTime - lane[i].GetFront().getTimeStamp());
// Increase the total wait time
mTotWaitTime += (mCurrentTime - lane[i].GetFront().getTimeStamp());
// Updates the cashiers time limit to process the current customer
cashiers[i].setTimeLimit( lane[i].GetFront().getServiceTime() );
// Removes the front customer from the lane to be processed
lane[i].Dequeue();
// Updates the cashier information
cashiers[i].reduceCustInLine();
cashiers[i].setAvailability(false);
cashiers[i].reduceTimeLimit();
}
// If cashier is not available and lane has at least one customer
else if ( !cashiers[i].isAvailable() && lane[i].isEmpty() )
{
// The cashier processes the
if ( cashiers[i].getTimeLimit() == 0 )
{
// Display log info
logMsg("Processed in lane", mCurrentTime, mNumberServiced, i);
// Updates the cashier information
cashiers[i].setAvailability(true);
cashiers[i].reduceCustInLine();
mNumberServiced++;
}
else
{
cashiers[i].reduceTimeLimit();
}
}
// If cashier is not available and lane has at least one customer
else if ( !cashiers[i].isAvailable() && !lane[i].isEmpty() )
{
if ( cashiers[i].getTimeLimit() == 0 )
{
// Display log info
logMsg("Wait time", mCurrentTime,
lane[i].GetFront().getCustNum(),
mCurrentTime - lane[i].GetFront().getTimeStamp());
mTotWaitTime += (mCurrentTime -
lane[i].GetFront().getTimeStamp());
cashiers[i].setTimeLimit( lane[i].GetFront().getServiceTime() );
// Removes the front customer from the lane to be processed
lane[i].Dequeue();
// Display log info
logMsg("Processed in lane", mCurrentTime, mNumberServiced, i);
// Updates the cashier information
cashiers[i].reduceCustInLine();
cashiers[i].reduceTimeLimit();
mNumberServiced++;
}
else
{
cashiers[i].reduceTimeLimit();
}
}
}
}
// Prints out the results of the simulation and creates an output file
void ServiceSimulator::OutputResults()
{
string title = "Simulation results";
string custServiced = "Total number of customers serviced: ";
string custTurnedAway = "Total number of customers turned away: ";
string aveWaitTime = "Average wait time: ";
string totWaitTime = "Total wait time: ";
string fileName = "Output.txt";
// Opens Text file and saves each part until inventory is complete.
ofstream saveFile(fileName.c_str());
if (saveFile.is_open())
{
saveFile << title << endl << endl;
saveFile << custServiced << mNumberServiced << endl;
saveFile << custTurnedAway << mTurnedAway << endl;
saveFile << aveWaitTime << AveWaitTime() << endl;
saveFile << totWaitTime << mTotWaitTime << endl;
}
saveFile.close();
cout << title << endl << endl;
cout << custServiced << mNumberServiced << endl;
cout << custTurnedAway << mTurnedAway << endl;
cout << aveWaitTime << AveWaitTime() << endl;
cout << totWaitTime << mTotWaitTime << endl;
cout << " File Saved" << endl;
}
// Calculates the average time the customers waited before being serviced.
float ServiceSimulator::AveWaitTime()
{
return mTotWaitTime / mNumberServiced;
}
// Generates a random time between the min and max time entered.
int ServiceSimulator::TimeGenerator(int minTime, int maxTime)
{
int range = maxTime - minTime + 1;
return rand() % range + minTime;
}
// Work in progress, once completed it will create a series of generations to
// create return more accurate results
void ServiceSimulator::SimulateSeries(ServiceSimulator parameters)
{
ServiceSimulator sims[1000] = ServiceSimulator(parameters);
int custServiced = 0;
int turnedAway = 0;
int aveWaitTime = 0;
int totWaitTime = 0;
for ( int i = 0; i < 1000; i++ )
{
cout << "Simulation " << i << endl;
sims[i].simulate();
custServiced += sims[i].getnumberServiced();
turnedAway += sims[i].getTurnedAway();
totWaitTime += sims[i].getTotWaitTime();
}
aveWaitTime = totWaitTime / custServiced;
string title = "Complete Simulation results after ";
string custServicedSt = "Total number of customers serviced: ";
string custTurnedAway = "Total number of customers turned away: ";
string aveWaitTimeSt = "Average wait time: ";
string totWaitTimeSt = "Total wait time: ";
cout << title << 1000 << " days" << endl << endl;
cout << custServicedSt << custServiced << endl;
cout << custTurnedAway << turnedAway << endl;
cout << aveWaitTimeSt << aveWaitTime << endl;
cout << totWaitTimeSt << totWaitTime << endl;
}
// Display the output message, along with the time (period), the customer number
void ServiceSimulator::logMsg(string output, int period, int custNum, int info)
{
if ( mParms.displayLogs )
{
if (period != 0 && custNum != 0)
cout << "NOTE: Time " << period << ": Customer " << custNum <<
": " << output << " - " << info << endl;
else
cout << "NOTE: Time " << period << endl;
}
}
--------------------------------------------------------------------------------------------------------------------
serviceSimulator.hpp
--------------------------------------------------------
#ifndef SERVICESIMULATOR_HPP_
#define SERVICESIMULATOR_HPP_
#include "queue.hpp"
#include "cashier.hpp"
#include <string>
using namespace std;
struct ServiceParms
{
// The number of cashiers to be stationed
int numOfCashiers;
// The maximum number of customers per lane
int lineMax;
// The total minutes in a single day
int minInDay;
// True to read customers from file or false to generate daily customers
bool readInput;
// The minimum and maximum arrival time
int arrTmMin;
int arrTmMax;
// The minimum and maximum service time
int serTmMin;
int serTmMax;
// Display the logs
bool displayLogs;
};
class ServiceSimulator
{
private:
// The parameters entered by the user for the simulation
ServiceParms mParms;
// The time of day in minutes.
int mCurrentTime;
// The number of customers serviced.
int mNumberServiced;
// The number of customers turned away from service due to no open lines.
int mTurnedAway;
// The total number of minutes customers had to wait.
int mTotWaitTime;
public:
ServiceSimulator(ServiceParms parms);
ServiceParms getServParms() { return mParms; };
int getCurrentTime() { return mCurrentTime; };
int getnumberServiced() { return mNumberServiced; };
int getTurnedAway() { return mTurnedAway; };
int getTotWaitTime() { return mTotWaitTime; };
void setServParms(ServiceParms parms) { mParms = parms; };
void setCurrentTime(int time) { mCurrentTime = time; };
void setNumberServiced(int num) { mNumberServiced = num; };
void setTurnedAway(int num) { mTurnedAway = num; };
void setTotWaitTime(int time) { mTotWaitTime = time; };
// Carries out the service simulation based on the entered parameters.
void simulate();
// Generates a list of all customers to enter in a single day using the
// parameters provided.
Queue PopulateCustomers();
// Calculates the average time the customers waited before being serviced.
float AveWaitTime();
// Prints out the results of the simulation and creates an output file.
void OutputResults();
// Display the output message, along with the time (period), the customer
// number and an additional integer if needed
void logMsg(string output, int period = 0, int custNum = 0, int info = 0);
private:
// Read input from a file or generate a queue of daily customers
void readInputCust(Queue* dailyCustomers);
// Allows the cashier to process the customers from the lane and records the
// specific data
void processCustomer(Queue lane[], Cashier cashiers[]);
// Generates a random time between the min and max time entered.
int TimeGenerator(int minTime, int maxTime);
// Increments the current time by 1
void incrementTime() {
mCurrentTime++;
}
// WIP, Generates a series of simulations for further accurate results
void SimulateSeries(ServiceSimulator parameters);
};
#endif /* SERVICESIMULATOR_HPP_ */
-----------------------------------------------------------------------------------------------------
Cashier.cpp
---------------------------------------------------
#include "cashier.hpp"
using namespace std;
Cashier::Cashier()
{
mCustInLine = 0;
mTimeLimit = 0;
mMaxInLine = 0;
mIsAvailable = true;
}
Cashier::Cashier(int lineMax)
{
mCustInLine = 0;
mTimeLimit = 0;
mMaxInLine = lineMax;
mIsAvailable = true;
}
// Determines if the line is full.
bool Cashier::isFull()
{
if (mCustInLine == mMaxInLine)
return true;
else
return false;
}
// Determines if the cashier is available
bool Cashier::isAvailable()
{
if ( mIsAvailable == true)
return true;
else
return false;
}
-----------------------------------------------------------------------------------------------------
Cashier.hpp
-----------------------------------------
#ifndef CASHIER_HPP_
#define CASHIER_HPP_
using namespace std;
class Cashier
{
private:
// The number of customers currently in the line including the customer
// being processed.
int mCustInLine;
// The number of minutes until the customer in the front of the line has
// been processed
int mTimeLimit;
// The max number of customers in line
int mMaxInLine;
// True if the cashier isn't working with any customers
bool mIsAvailable;
public:
Cashier();
Cashier(int lineMax);
int getCustInLine() { return mCustInLine; };
int getTimeLimit() { return mTimeLimit; };
int getMaxInLine() { return mMaxInLine; };
void setCustInLine(int cust) { mCustInLine = cust; };
void reduceCustInLine() { mCustInLine--; };
void increaseCustInLine() { mCustInLine++; };
void setTimeLimit(int timeLimit) { mTimeLimit = timeLimit; };
void reduceTimeLimit() { mTimeLimit--; };
void setMaxInLine(int lineMax) { mMaxInLine = lineMax; };
void setAvailability(bool isAvail) { mIsAvailable = isAvail; };
// Determines if the line is full.
bool isFull();
// Determines if the cashier is available
bool isAvailable();
};
#endif /* CASHIER_HPP_ */
--------------------------------------------------------------------------------------------------------
Customer.cpp
--------------------------------------------------
#include <atomic>
#include "customer.hpp"
using namespace std;
Customer::Customer()
{
mCustNum = 0;
mTimeStamp = 0;
mServiceTime = 0;
mWaitTime = 0;
}
Customer::Customer(int timeStamp, int serviceTime)
{
mCustNum = 0;
mTimeStamp = timeStamp;
mServiceTime = serviceTime;
mWaitTime = 0;
}
Customer::Customer(int custNum, int timeStamp, int serviceTime)
{
mCustNum = custNum;
mTimeStamp = timeStamp;
mServiceTime = serviceTime;
mWaitTime = 0;
}
-----------------------------------------------------------------------------------------------
Customer.hpp
-------------------------------------------
#ifndef CUSTOMER_HPP_
#define CUSTOMER_HPP_
#include "node.hpp"
#include <string>
using namespace std;
class Customer
{
private:
// The order in which the customer enters
int mCustNum;
// The minute that the customer enters the store
int mTimeStamp;
// The time taken for the cashier to service the customer
int mServiceTime;
// The time waiting in line to be processed.
int mWaitTime;
public:
Customer();
Customer(int timeStamp, int serviceTime);
Customer(int custNum, int timeStamp, int serviceTime);
int getCustNum() { return mCustNum; };
int getTimeStamp() { return mTimeStamp; };
int getServiceTime() { return mServiceTime; };
void setCustNum(int custNum) { mCustNum = custNum; };
void setTimeStamp(int timeStamp) { mTimeStamp = timeStamp; };
void setServiceTime(int serviceTime) { mServiceTime = serviceTime; };
};
#endif /* CUSTOMER_HPP_ */
--------------------------------------------------------------------------------------------------------
Node.cpp
-----------------------------------------------
#include "node.hpp"
#include "customer.hpp"
#include <string>
using namespace std;
Node::Node()
{
mCustomer = Customer();
mNextNode = NULL;
}
Node::Node(Customer cust)
{
mCustomer = cust;
mNextNode = NULL;
}
Node::Node(Customer cust, Node* nextNode)
{
mCustomer = cust;
mNextNode = nextNode;
}
----------------------------------------------------------------------------------------------------
Node.hpp
----------------------------------------------------
#include "customer.hpp"
using namespace std;
#ifndef NODE_HPP_
#define NODE_HPP_
class Node
{
private:
// Pointer of a customer
Customer mCustomer;
// Pointer to next node in stack
Node* mNextNode;
public:
Node();
Node(Customer cust);
Node(Customer cust, Node* nextNode);
void setNextPointer(Node *prt) { mNextNode = prt; };
void setCust(Customer cust) { mCustomer = cust; };
Node* getNextPointer() { return mNextNode; };
Customer getCust() { return mCustomer; };
};
#endif /* NODE_HPP_ */
--------------------------------------------------------------------------------------
queue.cpp
-----------------------------------------
#include "node.hpp"
#include "customer.hpp"
#include "queue.hpp"
#include <string>
using namespace std;
Queue::Queue()
{
mFront = NULL;
mRear = NULL;
mCount = 0;
}
// Adds new node to queue with entered
void Queue::Enqueue(Customer cust)
{
Node* newNode = new Node(cust);
if (isEmpty())
{
mFront = mRear = newNode;
}
else
{
mRear->setNextPointer(newNode);
mRear = newNode;
}
mCount++;
}
// Removes the first customer node in list
void Queue::Dequeue()
{
if (!isEmpty())
{
Node* ptr = mFront;
mFront = ptr->getNextPointer();
delete ptr;
}
}
// Returns the customer pointer first in the queue
Customer Queue::GetFront()
{
return mFront->getCust();
}
// Returns the customer pointer last in the queue
Customer Queue::GetRear()
{
return mRear->getCust();
}
// Determines if queue is empty or not
bool Queue::isEmpty()
{
if (mFront == NULL)
return true;
else
return false;
}
----------------------------------------------------------------------------------------
queue.hpp
------------------------------------------------
#ifndef QUEUE_HPP_
#define QUEUE_HPP_
#include "node.hpp"
#include "customer.hpp"
#include <string>
using namespace std;
class Queue
{
private:
// Constains the address of the first node
Node* mFront;
// Contains the address of the last node.
Node* mRear;
// The number of the nodes in queue
int mCount;
public:
Queue();
Node* getFrontNode() { return mFront; };
Node* getRearNode() { return mRear; };
int getCount() { return mCount; };
void setFrontNode(Node* ptr) { mFront = ptr; };
void setRearNode(Node* ptr) { mRear = ptr; };
void setCount(int count) { mCount = count; };
// Adds new node to queue with entered customer
void Enqueue(Customer cust);
// Removes the first customer node in list
void Dequeue();
// Returns the customer pointer first in the queue
Customer GetFront();
// Returns the customer pointer last in the queue
Customer GetRear();
// Determines if queue is empty or not
bool isEmpty();
};
#endif /* QUEUE_HPP_ */
Related Questions
drjack9650@gmail.com
Navigate
Integrity-first tutoring: explanations and feedback only — we do not complete graded work. Learn more.