//An implement of CSCI-1200 HW2 Ride Sharing //Author: Jinshan Zhou //Date: 2025/1/20 //#include //#include #include #include #include #include #include #include #include #include "Driver.h" #include "Rider.h" void debug_print(const std::string &msg) { std::cout << "DEBUG: " << msg << std::endl; } bool isPhoneNumberValid(const std::string &phone) { if (phone.size() != 12) return false; //xxx-xxx-xxxx for (int i = 0; i < 12; i++) { if (i == 3 || i == 7) { if (phone[i] != '-') return false; } else { if (!std::isdigit((unsigned char)phone[i])) return false; } } return true; } void loadDrivers(std::ifstream &ifs, std::vector &drivers) { //read the file line by line, total of 13 while (!ifs.eof()) { std::string fName, lName, gender, phone, vehicleType, state; std::string rF, rL, rP; int age; double rating, lat, lon; ifs >> fName >> lName >> gender >> age >> phone >> rating >> lat >> lon >> vehicleType >> state >> rF >> rL >> rP; if (!ifs.fail()) { //change "null" to empty string if (rF == "null") rF = ""; if (rL == "null") rL = ""; if (rP == "null") rP = ""; //create driver Driver d(fName, lName, gender, age, phone, rating, lat, lon, vehicleType, state, rF, rL, rP); drivers.push_back(d); } } ifs.close(); } void loadRiders(std::ifstream &ifs, std::vector &riders) { //read the file line by line, total of 17 while (!ifs.eof()) { std::string fName, lName, gender, phone, pickupLoc, dropoffLoc, vPref, state; std::string dF, dL, dP; int age; double rating, pickupLat, pickupLon, dropoffLat, dropoffLon; ifs >> fName >> lName >> gender >> age >> phone >> rating >> pickupLoc >> pickupLat >> pickupLon >> dropoffLoc >> dropoffLat >> dropoffLon >> vPref >> state >> dF >> dL >> dP; if (!ifs.fail()) { //fill null with empty string if (dF == "null") dF = ""; if (dL == "null") dL = ""; if (dP == "null") dP = ""; //create rider Rider r(fName, lName, gender, age, phone, rating, pickupLoc, pickupLat, pickupLon, dropoffLoc, dropoffLat, dropoffLon, vPref, state, dF, dL, dP); riders.push_back(r); } } ifs.close(); } std::ifstream loadFile(const std::string &filename) { //read file and return ifstream std::ifstream ifs(filename); if (!ifs) { std::cerr << "Error opening file: " << filename << std::endl; exit(1); } return ifs; } void saveFile(const std::string &filename, const std::string &msg) { std::ofstream ofs(filename); if (!ofs) { std::cerr << "Error opening file: " << filename << std::endl; exit(1); } ofs << msg; ofs.close(); } void exportDrivers(const std::string &filename, const std::vector &drivers) { //save drivers to file std::ofstream ofs(filename); if (!ofs) { std::cerr << "Error opening output file: " << filename << std::endl; return; } for (int i = 0; i < (int)drivers.size(); i++) { const Driver &d = drivers[i]; ofs << d.toFileString() << "\n"; } ofs.close(); } void exportRiders(const std::string &filename, const std::vector &riders) { //save riders to file std::ofstream ofs(filename); if (!ofs) { std::cerr << "Error opening output file: " << filename << std::endl; return; } for (int i = 0; i < (int)riders.size(); i++) { const Rider &r = riders[i]; ofs << r.toFileString() << "\n"; } ofs.close(); } // calculate the distance between two coordinates using Haversine formula double calculateDistance(double lat1, double lon1, double lat2, double lon2) { const double radiusOfEarth = 6371.0; // Earth's radius in kilometers // convert latitude and longitude from degrees to radians lat1 *= M_PI / 180.0; lon1 *= M_PI / 180.0; lat2 *= M_PI / 180.0; lon2 *= M_PI / 180.0; // Haversine formula double dLat = lat2 - lat1; double dLon = lon2 - lon1; double a = sin(dLat / 2.0) * sin(dLat / 2.0) + cos(lat1) * cos(lat2) * sin(dLon / 2.0) * sin(dLon / 2.0); double c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a)); // distance in kilometers double distanceKM = radiusOfEarth * c; // convert it to distance in miles double distanceMiles = distanceKM * 0.621371; return distanceMiles; } int findClosestDriver(const std::vector &drivers, const Rider &rider) { double minDistance = 1e9; int bestIndex = -1; for (int i = 0; i < (int)drivers.size(); i++) { const Driver &drv = drivers[i]; if (drv.getCurrentState() == "Available" && drv.getVehicleType() == rider.getVehiclePref()) { double dist = calculateDistance(drv.getLatitude(), drv.getLongitude(), rider.getPickupLatitude(), rider.getPickupLongitude()); if (dist < minDistance) { minDistance = dist; bestIndex = i; } } } return bestIndex; } int findRiderIndexByPhone(const std::vector &riders, const std::string &phone) { for (int i = 0; i < (int)riders.size(); i++) { if (riders[i].getPhoneNumber() == phone) { return i; } } return -1; } int findDriverIndexByPhone(const std::vector &drivers, const std::string &phone) { for (int i = 0; i < (int)drivers.size(); i++) { if (drivers[i].getPhoneNumber() == phone) { return i; } } return -1; } 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"; } int main(int argc, char *argv[]) { //take 3 arguments if (argc < 8) { std::cout << "Usage: nyride drivers.txt riders.txt output0.txt output1.txt output2.txt phoneNumber [request|cancel]\n" << std::endl; return 1; } //load arguments const std::string drivers_fName = argv[1]; const std::string riders_fName = argv[2]; std::string msg_fName = argv[3]; std::string updated_drivers_fName = argv[4]; std::string updated_riders_fName = argv[5]; std::string phone_number = argv[6]; std::string command = argv[7]; //turn on debug mode is last argument is debug bool debug_mode = false; if (std::string(argv[argc - 1]) == "debug") { debug_mode = true; } if (debug_mode) { debug_print("drivers_fName = " + drivers_fName); debug_print("riders_fName = " + riders_fName); debug_print("msg_fName = " + msg_fName); debug_print("updated_drivers_fName = " + updated_drivers_fName); debug_print("updated_riders_fName = " + updated_riders_fName); debug_print("phone_number = " + phone_number); debug_print("command = " + command); } //load drivers std::vector drivers; std::ifstream drivers_file = loadFile(drivers_fName); loadDrivers(drivers_file, drivers); //load riders std::vector riders; std::ifstream riders_file = loadFile(riders_fName); loadRiders(riders_file, riders); if (debug_mode) { debug_print("drivers.size() = " + std::to_string(drivers.size())); debug_print("riders.size() = " + std::to_string(riders.size())); } //check if phone number is valid std::ostringstream msg; if (!isPhoneNumberValid(phone_number)) { std::cout << "Error: Invalid phone number" << std::endl; msg << "Phone number is invalid.\n"; saveFile(msg_fName, msg.str()); return 1; } //check if command is valid if (command != "request" && command != "cancel") { std::cout << "Error: Invalid command" << std::endl; return 1; } else if (command == "request") { //for request cases int rIdx = findRiderIndexByPhone(riders, phone_number); if (rIdx == -1) { //if rider does not exist msg << "Account does not exist.\n"; saveFile(msg_fName, msg.str()); return 1; } Rider &r = riders[rIdx]; //check rider if (r.getCurrentState() == "Driver_on_the_way") { msg << "You have already requested a ride and your driver is on the way to the pickup location.\n"; saveFile(msg_fName, msg.str()); return 1; } if (r.getCurrentState() == "During_the_trip") { msg << "You can not request a ride at this moment as you are already on a trip.\n"; saveFile(msg_fName, msg.str()); return 1; } if (r.getCurrentState() == "Ready_to_request") { 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"; //find closest driver int dIdx = findClosestDriver(drivers, r); if (dIdx == -1) { //no driver msg << "Sorry we can not find a driver for you at this moment.\n"; } else { Driver &d = drivers[dIdx]; double dist = calculateDistance(d.getLatitude(), d.getLongitude(), r.getPickupLatitude(), r.getPickupLongitude()); dist = (int)(dist * 10) / 10.0; //cut to 1 decimal msg << "We have found the closest driver " << d.getFirstName() << "(" << std::fixed << std::setprecision(1) << d.getRating() << ") for you.\n" << d.getFirstName() << " is now " << std::fixed << std::setprecision(1) << dist << " miles away from you.\n"; //update status d.setCurrentState("On_the_way_to_pickup"); d.setRiderInfo(r.getFirstName(), r.getLastName(), r.getPhoneNumber()); r.setCurrentState("Driver_on_the_way"); r.setDriverInfo(d.getFirstName(), d.getLastName(), d.getPhoneNumber()); } } } else if (command == "cancel") { //for cancel cases //find phone_number in riders int rIdx = findRiderIndexByPhone(riders, phone_number); if (rIdx == -1) { //in case of driver's cancel int dIdx = findDriverIndexByPhone(drivers, phone_number); if (dIdx == -1) { //in case of not both rider and driver msg << "Account does not exist.\n"; saveFile(msg_fName, msg.str()); return 1; } Driver &driver = drivers[dIdx]; if (driver.getCurrentState() != "On_the_way_to_pickup") { msg << "You can only cancel a ride request if you are currently on the way to the pickup location.\n"; saveFile(msg_fName, msg.str()); return 1; } //get rider's phone number std::string rPh = driver.getRiderPhoneNumber(); //clean driver's rider info driver.setCurrentState("Available"); driver.setRiderInfo("", "", ""); msg << "Your driver " << driver.getFirstName() << " has canceled the ride request. We will now find a new driver for you.\n"; //find rider int theRiderIdx = findRiderIndexByPhone(riders, rPh); Rider &r = riders[theRiderIdx]; //reset rider r.setCurrentState("Ready_to_request"); r.setDriverInfo("", "", ""); //find a new driver 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"; int newDIdx = findClosestDriver(drivers, r); if (newDIdx == -1) { msg << "Sorry we can not find a driver for you at this moment.\n"; } else { Driver &newDriver = drivers[newDIdx]; double dist = calculateDistance(newDriver.getLatitude(), newDriver.getLongitude(), r.getPickupLatitude(), r.getPickupLongitude()); dist = (int)(dist * 10) / 10.0; //cut to 1 decimal msg << "We have found the closest driver " << newDriver.getFirstName() << "(" << std::fixed << std::setprecision(1) << newDriver.getRating() << ") for you.\n" << newDriver.getFirstName() << " is now " << std::fixed << std::setprecision(1) << dist << " miles away from you.\n"; //update driver's status newDriver.setCurrentState("On_the_way_to_pickup"); newDriver.setRiderInfo(r.getFirstName(), r.getLastName(), r.getPhoneNumber()); //update rider's status r.setCurrentState("Driver_on_the_way"); r.setDriverInfo(newDriver.getFirstName(), newDriver.getLastName(), newDriver.getPhoneNumber()); } } else { //in case of rider's cancel Rider &rider = riders[rIdx]; if (rider.getCurrentState() != "Driver_on_the_way") { msg << "You can only cancel a ride request if your driver is currently on the way to the pickup location.\n"; saveFile(msg_fName, msg.str()); return 1; } //find driver's phone_number std::string dPh = rider.getDriverPhoneNumber(); msg << "Ride request for rider " << rider.getFirstName() << " is now canceled by the rider.\n"; //set driver's status to Available int dIdx = findDriverIndexByPhone(drivers, dPh); if (dIdx != -1) { Driver &drv = drivers[dIdx]; drv.setCurrentState("Available"); drv.setRiderInfo("", "", ""); } //set rider's status to Ready_to_request rider.setCurrentState("Ready_to_request"); rider.setDriverInfo("", "", ""); } } //save msg saveFile(msg_fName, msg.str()); //save updated drivers and riders exportDrivers(updated_drivers_fName, drivers); exportRiders(updated_riders_fName, riders); return 0; }