CSCI 1200 - Homework 2 - Designing a Simple Uber
Assignment Requirements
Details
In this assignment you will develop a simple ride sharing application called New York Ride. Please read the entire handout before starting to code the assignment.
Learning Objectives
- Practice implementing and working with C++ classes.
- Practice using std::string, std::vector.
Specification
The New York Ride application should support 2 different roles: drivers, riders. Riders can perform two tasks:
- Request a ride
- Cancel a ride request
Drivers can perform one task:
- Cancel a ride request
Note: A commercial ride sharing product like Uber or Lyft of course allows riders and drivers to perform more tasks, but let’s be honest, Uber/Lyft has thousands of software engineers, but you only have one person and only have one week to work on this assignment, so let’s simplify the tasks.
Input Files
Companies like Uber and Lyft maintain all drivers and riders information in their database, but database is way beyond the scope of this course, and therefore we will just store drivers information and riders information in two simple text files, drivers.txt and riders.txt. In this assignment you will once again read these files as the input of your program, parse them so as to retrieve drivers and/or riders information, and store them in your own data structures. In this assignment, you must use std::vector to store drivers and riders. You are recommended to use one std::vector instance to store all drivers, use another std::vector instance to store all riders.
Driver Information
The drivers.txt has a format like this:
|
|
The above is the first 10 lines of the drivers.txt file. It has 13 fields, separated by a space. And these 13 fields are:
- Driver’s first name
- Driver’s last name
- Driver’s gender
- Driver’s age
- Driver’s phone number
- Driver’s rating
- Driver’s current latitude
- Driver’s current longitude
- Driver’s vehicle type
- Driver’s current state
- Rider’s first name
- Rider’s last name
- Rider’s phone number
The last 3 fields will only be meaningful if a ride request is assigned to this driver. In this assignment, we assume that drivers will accept the request whenever it is assigned to this driver.
A driver can be in one of the following states:
- Available (waiting for a request)
- On the way to a pickup location (request accepted)
- During a trip
When the driver is in an Available state, it means this driver is not assigned a ride request, and therefore is not associated with any rider, and as such, the last 3 fields of this driver will just be
|
|
Rider Information
The riders.txt has a format like this:
|
|
The above is the first 10 lines of the riders.txt file. It has 17 fields, separated by a space. And these 17 fields are:
- Rider’s first name
- Rider’s last name
- Rider’s gender
- Rider’s age
- Rider’s phone number
- Rider’s rating
- The name of the rider’s pickup location
- The latitude of the rider’s pickup location
- The longitude of the rider’s pickup location
- The name of the rider’s dropoff location
- The latitude of the rider’s dropoff location
- The longitude of the rider’s dropoff location
- Rider’s vehicle preference
- Rider’s current state
- Driver’s first name
- Driver’s last name
- Driver’s phone number
A rider can be in one of the following states:
- Ready to request
- Driver on the way (to pickup)
- During a trip
Ideally, there should be four states, and this other state would be: Ride requested but not yet accepted by any driver. However, as we mentioned, in this assignment, we assume that when a rider issues a request, it will be accepted by a driver, and thus we can exclude this state from our consideration.
When the rider is in Ready_to_request state, it means no driver is now assigned to this ride request, and therefore, the last 3 fields of this rider will just be
|
|
Commands to Support
Your program only needs to support two commands:
Ride Request
The first command allows the rider can to send a ride request.
|
|
Here
- drivers.txt is the input file which contains all drivers’ information. Your program should never change this file.
- riders.txt is the input file which contains all riders’ information. Your program should never change this file.
- output0.txt is the output file where you print messages to rider or driver.
- output1.txt is the output file where you print the updated drivers information, thus this file should have the same format as drivers.txt.
- output2.txt is the output file where you print the updated riders information, thus this file should have the same format as riders.txt.
- phoneNumber. Ideally this should be a phone number which corresponds to one of the riders in the riders.txt whose state is “Ready_to_request”; but life is not always ideal, and how your program should cope with various phone number cases will be described in this section.
- request indicates this is a ride request.
When this command is run, and
- if a driver is found, your program should
1.1 print the following information into the output0.txt file:
|
|
Replace Rebecca with the rider’s first name, replace Economy with the rider’s preferred vehicle type, replace Williamsburg with the rider’s pickup location, and replace Statue_of_Liberty with the rider’s drop off location. Replace Elena with the driver’s first name, replace 4.7 with the driver’s rating. Replace 7.9 with the driver’s distance from the rider.
1.2 print an updated version of drivers.txt into output1.txt.
1.3 print an updated version of riders.txt into output2.txt.
- if a driver can not be found, your program should print the following message into the output0.txt file:
|
|
Replace Isabella with the rider’s first name, replace Luxury with the rider’s preferred vehicle type, replace Williamsburg with the rider’s pickup location, and replace Boerum_Hill with the rider’s drop off location.
- if the phone number provided from the command line is not in the format of xxx-xxx-xxxx, your program should print the following message to the output0.txt file:
|
|
- if the phone number provided from the command line does not match with any of the riders’ phone numbers, your program should print the following message to the output0.txt file:
|
|
- if the rider who is issuing this request is in a state of “Driver_on_the_way”, your program should print the following message to the output0.txt file:
|
|
- if the rider who is issuing this request is in a state of “During_the_trip”, your program should print the following message to the output0.txt file:
|
|
Canceling a Request
The second command allows a rider or a driver to cancel the request. Keep in mind that both the rider and the driver has the right to cancel the request.
|
|
The only difference between this command and the first command is the last argument here is cancel, whereas in the first command, the last argument is request.
When a rider cancels a request, you should just cancel the request; when a driver cancels a request, you should cancel the request, but at the same time, find another closest driver for this rider.
Only drivers who are on the way to a pickup location, or riders whose driver is on the way, should be allowed to cancel a request.
When this second command is run, and
- if the phone number provided from the command line does not match with any of the riders’ phone numbers, and does not match with any of the drivers’ phone numbers, your program should print the following message to the output0.txt file:
|
|
- if the canceling request is issued by a rider whose state is NOT Driver_on_the_way, your program should print the following message to the output0.txt file:
|
|
- if the canceling request is issued by a driver whose state is NOT On_the_way_to_pickup, your program should print the following message to the output0.txt file:
|
|
- if the canceling request is issued by a rider whose state is Driver_on_the_way, your program should:
4.1 print the following message to the output0.txt file:
|
|
4.2 print an updated version of drivers.txt into output1.txt: the driver’s state should be changed from On_the_way_to_pickup to Available, and the last 3 fields of the driver should be reset to null, meaning that this driver is now not associated with any rider.
4.3 print an updated version of riders.txt into output2.txt: the rider’s state should be changed from Driver_on_the_way to Ready_to_request, and the last 3 fields of the rider should be reset to null, meaning that no driver is now associated with this rider.
- if the canceling request is issued by a driver whose state is On_the_way_to_pickup, your program should:
5.1 print the following message to the output0.txt file:
|
|
Replace Edward with the driver’s first name. Replace Angela with the rider’s first name, replace Standard with the rider’s preferred vehicle type. Replace The_Met_Cloisters with the rider’s pickup location, and replace Brooklyn_Navy_Yard with the rider’s drop off location. Replace Robert with the new driver’s first name. Replace 3.2 with the new driver’s rating. Replace 2.1 with the new driver’s distance to the rider.
5.2 print an updated version of drivers.txt into output1.txt: the old driver’s state should be changed from On_the_way_to_pickup to Available. A new driver should be assigned and that new driver’s state should be updated accordingly. Also the old driver should no longer be associated with this rider, and the new driver should now be associated with this rider.
5.3 print an updated version of riders.txt into output2.txt: the rider should now be associated with the new driver.
Calculate Distance Based on Haversine Formula
When finding the driver, you must always find the closest driver whose vehicle type matches with the rider’s preference. And when the closest driver is found, you also need to print the distance between this driver and the rider. Thus, you need a way to calculate the distance between two coordinates, and for that purpose, in this assignment, you will use the Haversine Formula, and the code of using the Haversine formula is given below:
|
|
This function takes four parameters, which are the latitude and longitude of two geographical locations, and this function returns the distance (in miles) between these two locations. This function calls several math library functions, and therefore you need to include the cmath library:
|
|
Include Guards
If you are writing more than one class, you may run into strange compiler errors when you compile everything. This may be due to a problem with including your class files, which can be solved as follows: for a header file called myclass.h add these two lines at the very top of the header file:
|
|
and at the very bottom of your .h file, add this line:
|
|
This technique is known as the “Include Guards”. Include guards ensure that the compiler will process a header file only once, no matter how many times it is included.
FAQs
- Q: Is the requested vehicle type from a rider’s perspective a strict requirement for finding a matching driver? Or is it just a preference. Essentially, if a rider requests Economy and there is no available drivers for Economy, but available drivers with other vehicle types, should we output that no driver could be found, or match the nearest driver with different vehicle type?
A: It is a strict requirement. Do not pick a different vehicle type for the rider.
- Q: What is the precision of the output distance? Is it one decimal place or significant figures or holding a certain number of spaces? Do we round up round down or simply trim it down?
A: Same as Uber. One decimal place. Just trim it. For example, if the distance is 11.4571 miles, you should output 11.4 miles, instead of 11.5 miles.
Program Requirements & Submission Details
In this assignment, you are required to use a vector to store all drivers, and use a vector to store all riders. You are NOT allowed to use any data structures we have not learned so far, especially std::list. Your program should involve the definition of at least two classes that have their own .h and .cpp files, named appropriately.
Use good coding style when you design and implement your program. Organize your program into functions: don’t put all the code in main! Be sure to read the Homework Policies as you put the finishing touches on your solution. Be sure to make up new test cases to fully debug your program and don’t forget to comment your code! Use the provided template README.txt file for notes you want the grader to read. You must do this assignment on your own, as described in the Collaboration Policy & Academic Integrity page. If you did discuss the problem or error messages, etc. with anyone, please list their names in your README.txt file.
Due Date: 01/23/2025, Thursday, 10pm.
Rubric
14 pts
- README.txt Completed (3 pts)
- One of name, collaborators, or hours not filled in. (-1)
- Two or more of name, collaborators, or hours not filled in. (-2)
- No reflection. (-1)
- OVERALL CLASS DECLARATION & IMPLEMENTATION AND CODING STYLE (Good class design, split into a .h and .cpp file. Functions > 1 line are in .cpp file. Organized class implementation and reasonable comments throughout. Correct use of const/const& and of class method const. ) (6 pts)
- No credit (significantly incomplete implementation) (-6)
- Does not successfully declare & use any new classes. (-6)
- Only declares/uses a single class. (-5)
- Putting almost everything in the main function. It’s better to create separate functions for different tasks. (-2)
- Improper uses or omissions of const and reference. (-1)
- Function bodies containing more than one statement are placed in the .h file. (okay for templated classes) (-2)
- Functions are not well documented or are poorly commented, in either the .h or the .cpp file. (-1)
- Overly cramped, excessive whitespace, or poor indentation. (-1)
- Poor file organization: Puts more than one class in a file (okay for very small helper classes) (-1)
- Poor choice of variable names: non-descriptive names (e.g. ‘vec’, ‘str’, ‘var’), single-letter variable names (except single loop counter), etc. (-2)
- Uses global variables. (-1)
- DATA REPRESENTATION (Must use vectors for the implementation.) (5 pts)
- No credit (significantly incomplete implementation). (-5)
- Does not use std::vector to store drivers or riders. (-5)
- Uses std::list or data structures which have not been covered in this class. (-5)
- Member variables are public. (-2)
Supporting Files
ride_sharing.7zProgram Design
Due to the complexity of this assignment, it is best to carefully plan how to implement it before starting. Here I will use two scenarios: “request” and “cancel”, respectively designing their individual processes, then using these parts in the main program. Let’s call them handleRequest()
and handleCancel()
.
Since the flowchart is quite large, I have temporarily converted it into an image.
Mermaid source code as follows:
|
|
With the flowchart, we can start implementing them step by step. Of course, you don’t necessarily need to write handleRequest()
and handleCancel()
as separate functions; implementing their logic directly in main()
is also acceptable. I am just using this approach for clarity.
Additionally, we need to design two classes because it’s a requirement of the assignment and it makes our implementation easier.
classDiagram class Rider { %% Data members - firstName : - lastName : string - gender : string - age : int - phoneNumber : string - rating : double - pickupLocationName : string - pickupLatitude : double - pickupLongitude : double - dropoffLocationName : string - dropoffLatitude : double - dropoffLongitude : double - vehiclePref : string - currentState : string - driverFirstName : string - driverLastName : string - driverPhoneNumber : string %% Constructors + Rider() %% Methods + getFirstName() + getLastName() + getGender() + getAge() + getPhoneNumber() + getRating() + getPickupLocationName() + getPickupLatitude() + getPickupLongitude() + getDropoffLocationName() + getDropoffLatitude() + getDropoffLongitude() + getVehiclePref() + getCurrentState() + getDriverFirstName() + getDriverLastName() + getDriverPhoneNumber() + setCurrentState() + setDriverInfo() + toFileString() } class Driver { %% Data members - firstName : string - lastName : string - gender : string - age : int - phoneNumber : string - rating : double - latitude : double - longitude : double - vehicleType : string - currentState : string - riderFirstName : string - riderLastName : string - riderPhoneNumber : string %% Constructors + Driver() %% Methods + getFirstName() + getLastName() + getGender() + getAge() + getPhoneNumber() + getRating() + getLatitude() + getLongitude() + getVehicleType() + getCurrentState() + getRiderFirstName() + getRiderLastName() + getRiderPhoneNumber() + setCurrentState() + setRiderInfo() + toFileString() }classDiagram class Rider { %% Data members - firstName : - lastName : string - gender : string - age : int - phoneNumber : string - rating : double - pickupLocationName : string - pickupLatitude : double - pickupLongitude : double - dropoffLocationName : string - dropoffLatitude : double - dropoffLongitude : double - vehiclePref : string - currentState : string - driverFirstName : string - driverLastName : string - driverPhoneNumber : string %% Constructors + Rider() %% Methods + getFirstName() + getLastName() + getGender() + getAge() + getPhoneNumber() + getRating() + getPickupLocationName() + getPickupLatitude() + getPickupLongitude() + getDropoffLocationName() + getDropoffLatitude() + getDropoffLongitude() + getVehiclePref() + getCurrentState() + getDriverFirstName() + getDriverLastName() + getDriverPhoneNumber() + setCurrentState() + setDriverInfo() + toFileString() } class Driver { %% Data members - firstName : string - lastName : string - gender : string - age : int - phoneNumber : string - rating : double - latitude : double - longitude : double - vehicleType : string - currentState : string - riderFirstName : string - riderLastName : string - riderPhoneNumber : string %% Constructors + Driver() %% Methods + getFirstName() + getLastName() + getGender() + getAge() + getPhoneNumber() + getRating() + getLatitude() + getLongitude() + getVehicleType() + getCurrentState() + getRiderFirstName() + getRiderLastName() + getRiderPhoneNumber() + setCurrentState() + setRiderInfo() + toFileString() }
Pitfalls
-
When printing
output0.txt
, the phrase “looking for” should be followed by a rider’s preferred vehicle type, which may contain vowels. Therefore, you need to determine whether to use “a” or “an”. This is a small issue but don’t forget it. I used a function to return “a” or “an”.1 2 3 4 5 6 7
std::string autoAAn(const std::string &word) { if (word.empty()) return ""; if (word[0] == 'A' || word[0] == 'E' || word[0] == 'I' || word[0] == 'O' || word[0] == 'U') { return "an"; } return "a"; }
Then, use
ostringstream
from<sstream>
to concatenate fields obtained from the Rider class. The following code demonstrates this logic and structure.1 2 3 4 5 6 7 8 9
#include <sstream> //... Rider &r = riders[rIdx]; //just consider `r` as your rider class std::ostringstream msg; msg << "Ride requested for rider " << r.getFirstName() << ", looking for " << autoAAn(r.getVehiclePref()) << " " << r.getVehiclePref() << " vehicle.\n" << "Pick Up Location: " << r.getPickupLocationName() << ", Drop Off Location: " << r.getDropoffLocationName() << ".\n";
-
Be very careful when handling driver states, as changes need to be updated immediately because some logic depends on the current state of the driver. For example, when a driver transitions from
On_the_way_to_pickup
toAvailable
, clear Rider associated information and automatically find a new Driver for the Rider. When re-locating a new Driver, avoid finding previously assigned Drivers. -
Class variables should not be public (as per assignment requirements); access them through methods rather than directly manipulating the variables.
-
According to the assignment requirements, we cannot use
auto
keyword, which necessitates modifying for-loops as follows:1 2 3 4 5 6 7 8 9 10
void exportDrivers(const std::string &filename, const std::vector<Driver> &drivers) { //... std::ofstream ofs(filename); - for (const auto &d : drivers) { + for (int i = 0; i < (int)drivers.size(); i++) { + const Driver &d = drivers[i]; ofs << d.toFileString() << "\n"; } ofs.close(); }
Solution
nyrider.cpp
|
|
Rider.h
|
|
Rider.cpp
|
|
Driver.h
|
|
Driver.cpp
|
|
Related Content
- CSCI 1100 - Homework 1 - Calculations and Strings
- CSCI 1100 - Homework 8 - Bears, Berries, and Tourists Redux - Classes
- CSCI 1100 - Homework 7 - Dictionaries
- CSCI 1100 - Homework 6 - Files, Sets and Document Analysis
- CSCI 1100 - Homework 5 - Lists of Lists; Grids; Path Planning