CS 2005 Techniques of Programming WPI, B Term 1996
Craig E. Wills Project 3 (35 pts)
Assigned: Thursday, November 21, 1996 Due: Monday, (11:59pm) December 9, 1996

Introduction

This assignment is designed for you to put into practice the concepts of linked lists, hashing and searching. You have been hired to implement an airline reservation system for FairAir (``we may only have fair air service, but we have fair air fares!). Your implementation will employ the various concepts. You must use the data structures defined in this handout. Although other implementations could be used, the ones specified here are designed to use specific concepts that you need to understand.

Basic Objective

The basic objective of your assignment is to build an airline reservation system that allows various operations of viewing flight information and making reservations. Your program must provide the following menu:

0. Print this menu.
1. Exit.
2. Show a list of cities serviced by FairAir (order not important).
3. Show flight departures for a city listed in order of time.
4. Show flight arrivals for a city listed in order of time.
5. Make an airline reservation for a passenger between two cities.
6. Print a list of passengers (in order of last name).
7. Print a passenger's reservation schedule.
8. Delete a passenger's reservation.

Flight Schedule Database

Everyone will use the same flight information, which is available in the file
/cs/cs2005/pub/example/fairairbig. You should not copy this file, but read it directly! This file contains information about all flights provided by FairAir. Each line of the file contains information on one flight. We will assume that the flight schedule is the same each day. A sample input file is contained in /cs/cs2005/pub/example/fairair (cities will contain no spaces):

334  Boston   657    Chicago  732
237  Boston   1224   Chicago  1315
417  Boston   1112   NewYork  1148
339  Chicago  1400   SanFrancisco 1549

The first line shows that flight number 334 departs Boston at 6:57am and arrives in Chicago at 7:32am. All times are in the local time of the city. You should use this small file until you have your program working then use the large flight file.

You will be creating a Flights C++ class to store all information about the flight database. All definitions in this handout can be found in /cs/cs2005/pub/example/airline.h. You can read each line of the file in the constructor function for the class similar to the following piece of code. Note that you can use the ``>>'' operator to read from a file just as you read from cin.

/*
 * Flights -- initialize the flightdb.
 */
Flights::Flights()
{
    ifstream flightfile(FLIGHTFILE); // create the stream variable for file
    int wFlight, tDeparture, tArrival;
    char sbCity1[MAXNAME], sbCity2[MAXNAME];
    
    if (!flightfile) {                // variable will be zero if error
        cerr << "Could not open the file " << FLIGHTFILE " for reading.\n";
        exit(1);         // exit the program with error code of one 
    }
    while (1) {
        flightfile >> wFlight >> sbCity1 >> tDeparture >> sbCity2 >> tArrival;
        if (flightfile.eof())    // must check for eof after each read
            break;                 // out of the loop
        // at this point add code to put flight in database
    }

    flightfile.close();                // close the file
}

For each flight you will create a structure that contains information about the flight. The definition for this structure is:

struct FlightType {
    int wFlight;                /* flight number */
    char *sbCity1;                /* departure city */
    int tDeparture;                /* departure time hhmm, e.g. 623 or 1258 */
    char *sbCity2;                /* arrival city */
    int tArrival;                /* arrival time hhmm, e.g. 623 or 1258 */
    int cPassenger;                /* number of passengers on the flight */
    FlightType *pFlightDep;     /* next departure node from this city */
    FlightType *pFlightArr;     /* next arrival node to this city */
};

You should write a MakeFlightNode() member function to create and initialize a flight. Note: You will need to use new for creating space for the node and the strings of the city names. For creating space for strings do not forget to use brackets such as
``new char[strlen(sbCity1)+1].''

/*
 * MakeFlightNode -- create a flight node from the given info
 */
FlightType *Flights::MakeFlightNode(int wFlight, char *sbCity1, int tDeparture,
                           char *sbCity2, int tArrive)

Once you have created a flight node you must link it into a list of departures for the departure city, a list of arrivals for the arrival city and a list of flights based on the flight number. The departure and arrival lists are maintained as a hashed array of cities with each array entry containing a pointer to the head of the list for departures and arrivals. This array is rgCity[] and must be declared as a private member of the Flights class:

#define MAXCITY 97               /* maximum number of cities to maintain */
struct CityListType {
    char *sbCity;                /* name of the city */
    FlightType *pFlightDep;        /* first departure from this city */
    FlightType *pFlightArr;        /* first arrival to this city */
};

    CityListType rgCity[MAXCITY];  /* array of cities */

You should initialize the sbCity field of all entries to NULL to indicate the entry is not being used. You will need to use a hash function (which we will discuss in class) to map a city to its index in the array. You will need to use new to allocate space for the city name (if it is a new city) and link the departure into the departure list for the city. Repeat for the arrival city. Each linked list should be sorted by time so that the first departure/arrival of the day is first in the list.

The other data structure contained within the Flights class is the array rgCity[], which is declared as follows:

#define MAXFLIGHT 199           /* maximum number of flights to maintain */
struct FlightNumberListType {
    int wFlight;                /* flight number */
    FlightType *pFlight;        /* flight node for this type */
};

    FlightNumberListType rgFlight[MAXFLIGHT];  /* array of flights */

It is a sorted array of elements based on the flight number. The list must be sorted to facilitate searching of flights based upon the flight number. Each element of the array also maintains a link to the flight node. You should maintain a count on the number of flights in the database.

A picture of the resulting flight database for the sample input is shown in Figure 1. This figure shows how each flight node is tied in with the rgFlight[] and rgCity[] arrays. Note: Your hash function will likely compute different indices for the cities shown.

flightdb Figure: Sample Structure of the Flight Database.

Passenger Schedule Database

You will also need to maintain a linked list of passenger reservations. These should be maintained in a Reservations class. You should use the following data structures within this class and the pReserveHead variable as a private member of the class.

struct RouteType {
    int wDay;                        /* day of travel: mmdd */
    int cHop;                        /* Number of hops (1 or 2) */
    int wFlight1;                /* Flight number of first hop */
    int wFlight2;                /* Flight number of second hop (if needed) */
};

#define ROUNDTRIP 0
#define ONEWAY 1
struct ReservationType {
    char *sbFirst;                /* first name of passenger */
    char *sbLast;                /* last name of passenger */
    int wType;                        /* ROUNDTRIP or ONEWAY */
    RouteType route1;                /* first route */
    RouteType route2;                /* second route (only if ROUNDTRIP) */
    ReservationType *pReserve;  /* next reservation in linked list */
};

    ReservationType *pReserveHead; /* head of the reservation list */

Your program will use the file reservations in the current directory to read and store reservations. Because FairAir is so popular we will restrict passengers to having no more than one reservation at a time. Two lines from a sample reservations data file are given below. The constructor function for the Reservations class should initially open this file for reading (similar to the constructor for the Flights class and using the ``>>'' operator for reading its contents). If the file does not exist then your program should not exit and continue with no reservations. A sample reservations file is as follows.

Tanya Tourist 0 1026 1 417 1028 1 98
Tony Traveler 1 1123 2 237 339

Each line has a variable number of fields depending on whether the trip is round trip or one-way and depending on the number of hops (flights) in a route. Tanya's reservation is round trip beginning 10/26 with travel on flight 417 and a return route on 10/28 with travel on flight 98 (not shown in the sample flight input). Tony has booked a one-way reservation for 11/23 that consists of two hops--flight 237 connecting to flight 339.

Once a reservation is read in, a reservation structure should be created for it using the member function MakeReserveNode(). This structure should be added to the linked list of reservations (in order of last name and then first name) where pReserveHead points to the first entry in the list.

/*
 * MakeReserveNode -- create a reservation node from the given info
 */
ReservationType *Reservations::MakeReserveNode(char *sbFirst, char *sbLast, 
                        int wType, RouteType route1, RouteType route2)

The last step before your program exits is to write out all passenger reservations using this same format with one reservation per line. You need to create a WriteReservations() member function to first open the file for writing and then using ``<<'' for writing the information to the file.

Creating a Reservation

The last major portion of the program is to create a reservation using the routine MakeReserve(). It needs to be passed the flight database so it can access the flight database functions while making a reservation.

/*
 * MakeReserve -- make a reservation for a passenger by reading info from
 *                the user.
 */
void Reservations::MakeReserve(Flights &flightdb)

In the routine, you will need to gather the name of the passenger and the departure city along with the type of travel (round trip or one-way) and the day(s) of travel. For each trip you will need to see if a flight can be booked based on the flight database. Passengers prefer direct flights between cities, but will accept a trip with one connecting flight. By default passengers will accept any trip you give them on the day of travel (no preferences for the time of day).

You should create a member function FindRoute() for computing a route between two cities.

/*
 * FindRoute -- find a route from city1 to city2 and put it in the route
 *              structure.  Return 0 if a route is found and -1 if no
 *              route could be found.
 */
int Reservations::FindRoute(char *sbCity1, char *sbCity2, RouteType &route,
                            Flights &flightdb)

The algorithm for FindRoute() should be:

  1. Get the list of departures for city1 and if there exist any direct flights to city2 then pick one of the flights and return it.

  2. If no direct flights exist then compare the departure list for city1 with the arrival list for city2. Try to find a city in common to both lists to use for connections. As a constraint, the second flight must leave at least MINLAYOVER minutes (default of 30) after the first flight arrives. Return any valid connection.

  3. If no flight combinations exist then report that FairAir will not be able to handle the passenger's travel needs and do not make the reservation.

As part of booking a flight you should increment the count of passengers on each flight within the flight database.

Other Member Functions

Below are other functions useful in writing this program. Prototypes for these functions are declared in the class definitions. You may need to create additional functions and class members. Sample code for CityDepartureList() is shown.

/*
 * CityDepartureList -- return a sorted list of departures for a city
 */
FlightType *Flights::CityDepartureList(char *sbCity)
{
    int i, iSave;

    iSave = i = Hash(sbCity);        /* compute index in hash table */
    while (rgCity[i].sbCity != NULL) {
        if (strcmp(sbCity, rgCity[i].sbCity) == 0)
            return(rgCity[i].pFlightDep);
        i = (i + 1)%MAXCITY;
        if (i == iSave)
            return(NULL);        /* have looped all the way around */
    }
    return(NULL);                /* city not in hash table */
}

/*
 * CityArrivalList -- return a sorted list of departures for a city
 */
FlightType *Flights::CityArrivalList(char *sbCity)

/*
 * FlightByNumber -- return info about a flight given its flight number
 */
FlightType *Flights::FlightByNumber(int wFlight)

/*
 * PrintPassengers -- print the list of passengers
 */
void Reservations::PrintPassengers()

/*
 * PrintSchedule -- prompt the user for a passenger name and print that
 *                passenger's schedule.
 */
void Reservations::PrintSchedule(Flights &flightdb)

/*
 * DeleteReserve -- delete a reservation for a passenger by prompting for name
 */
void Reservations::DeleteReserve()

Sample Output

The program should continually loop, requesting a command (input as an integer) and executing it. You should do error checking. A sample script for your program with the input data given in this handout might be (without the comments):

> 
Enter a command (2-8, 0-help, 1-exit): 2
SanFrancisco                      /* print non-NULL entries in rgCity[] */
Chicago
Boston
NewYork
Enter a command (2-8, 0-help, 1-exit): 3
Enter a city for its departures? Boston
 6:57am 334 Chicago (0)                  /* (0) indicates passenger count */
11:12am 417 NewYork (1)
12:24pm 237 Chicago (1)
Enter a command (2-8, 0-help, 1-exit): 4
Enter a city for its arrivals? Chicago
 7:32am 334 Boston (0)
 1:15pm 237 Boston (1)
Enter a command (2-8, 0-help, 1-exit): 5
Enter first name: Foo
Enter last name: Bar
Type of trip (0=Round-trip or 1=one-way)? 1
Month of Travel (1-12)? 12
Day of Travel (1-31)? 22
Departure City? Boston
Destination City? SanFrancisco
Flight confirmed:             /* could have selected earlier Boston flight */
Flight 237 12/22 depart Boston 12:24pm arrive Chicago 1:15pm
Flight 339 12/22 depart Chicago 2:00pm arrive SanFrancisco 3:39pm
Enter a command (2-8, 0-help, 1-exit): 6
Foo Bar
Tanya Tourist
Tony Traveler
Enter a command (2-8, 0-help, 1-exit): 7
Passenger's first name? Foo
Passenger's last name? Bar
Flight 237 12/22 depart Boston 12:24pm arrive Chicago 1:15pm
Flight 339 12/22 depart Chicago 2:00pm arrive SanFrancisco 3:39pm
Enter a command (2-8, 0-help, 1-exit): 8
Passenger's first name? Foo
Passenger's last name? Bar
Reservation deleted.
Enter a command (2-8, 0-help, 1-exit): 1
>

Group Organization

You should discuss the overall organization of the program and make sure that each member of the group understands the overall task to be done. You should also discuss the specifics of each class, including its data structures and the routines to manipulate them. If your group does not understand some aspect of the project you should consult with your PLA, a TA or the instructor. After the discussion your group should break the implementation into pieces. The two major portions of the project are handling the Flights class (which could be divided into handling the rgFlight[] and rgCity[] arrays) and handling the Reservations class (which could be divided into handling the passenger linked list and computing the route between two cities). You could have pairs of people working on each major portion. You will also need a driver program, which declares a Flights and Reservations class variable and implements the main menu to tie the pieces together. Each group member should have responsibility for implementing a portion of the code.

You should divide your program into modules that follow the data structures and routines that manipulate them. You should use a header file for definitions that are shared. The header file /cs/cs2005/pub/example/airline.h contains all definitions defined in this handout. You must also create a makefile to use in compiling your pieces of code together.

Additional Work

Completion of the basic objectives with satisfactory style and documentation is worth 29 of the 35 points. Use of a binary rather than linear search (to be discussed in class) for the FlightByNumber() routine is worth an additional 3 points.

The additional work for the last three points of your program is to allow two routing modes-- ``optimal'' and ``selection''. These modes should be toggled by an option 9 added to your main program. When optimal mode is set the FindRoute() routine should return the shortest travel time for either direct connections or routes with layovers (do not forget that you must have a minimum layover time). When the selection mode is set you should provide the user with a list of valid flights and allow the user to select a route. If direct connections exist you should only show direct connections and not any requiring layovers.

Submission

As an intermediate deadline, each group will use the gturnin command with the project name ``proj3design'' to turn in your initial design for working on the project. This should be a single text file indicating who has responsibility for each piece of the project and a schedule for completing the project. This design is due by class time on Tuesday, November 26.

You will be turning in your project as a group using the gturnin command with the name of ``proj3'' for the project. You will also turnin an individual assessment of your groups' efforts as done in the previous assignment. Again, you are expected to talk as a group about each member's contributions before submitting your individual assessments.