You are on page 1of 19

Ritesh Shukla(UI19EC39) & Vrishir Iyer(UI19EC54)

Artificial Intelligence (CS 701)


Lab Report Submitted to

Indian Institute of Information Technology Surat


for

Bachelor of Technology

In

Electronics and Communication Engineering Department


Submitted by

RITESH SHUKLA (UI19EC39)

VRISHIR BHASKAR IYER (UI19EC54)


Course Faculty

Dr. Pradeep Kumar Roy


Ms. Shraddha Patel
Department of Computer Science and Engineering
Indian Institute of Information Technology Surat
Gujarat-395007, India

December – 2022

1
TIME TABLE
AIM:

Build Time Table for IIIT SURAT

1.) Included libraries that are used for projects.

2.) Defined Classes for different attributes

THEORY (List of concepts used) :

Class: A class in C++ is the building block that leads to Object-Oriented programming. It is a
user-defined data type, which holds its own data members and member functions, which can be
accessed and used by creating an instance of that class. A C++ class is like a blueprint for an
object. For Example: Consider the Class of Cars. There may be many cars with different names
and brand but all of them will share some common properties like all of them will have 4
wheels, Speed Limit, Mileage range etc. So here, cars are the class and wheels, speed limits,
mileage are their properties.
● A Class is a user defined data-type which has data members and member functions.
● Data members are the data variables and member functions are the functions used to
manipulate these variables and together these data members and member functions
define the properties and behavior of the objects in a Class.
● In the above example of class Car, the data member will be speed limit, mileage etc
and member functions can be apply brakes, increase speed etc.

A class is defined in C++ using the keyword class followed by the name of the class. The body
of class is defined inside the curly brackets and terminated by a semicolon at the end.
An Object is an instance of a Class. When a class is defined, no memory is allocated but when it
is instantiated (i.e. an object is created) memory is allocated.

Declaring Objects: When a class is defined, only the specification for the object is defined; no
memory or storage is allocated. To use the data and access functions defined in the class, you
need to create objects

Accessing data members and member functions: The data members and member functions of
class can be accessed using the dot(‘.’) operator with the object. For example if the name of
object is obj and you want to access the member function with the name printName() then you
will have to write obj.printName() .

2
Accessing Data Members
The public data members are also accessed in the same way given however the private data
members are not allowed to be accessed directly by the object. Accessing a data member
depends solely on the access control of that data member.

There are three access modifiers : public, private and protected. 

There are 2 ways to define a member function:


● Inside class definition
● Outside class definition
To define a member function outside the class definition we have to use the scope resolution ::
operator along with class name and function name. 
Constructors are special class members which are called by the compiler every time an object
of that class is instantiated. Constructors have the same name as the class and may be defined
inside or outside the class definition. There are 3 types of constructors:
● Default constructors
● Parameterized constructors
● Copy constructors

Unordered_map is an associated container that stores elements formed by the combination of a


key value and a mapped value. The key value is used to uniquely identify the element and the
mapped value is the content associated with the key. Both key and value can be of any type
predefined or user-defined. In simple terms, an unordered_map is like a data structure of
dictionary type that stores elements in itself. It contains successive pairs (key, value), which
allows fast retrieval of an individual element based on its unique key.
Internally unordered_map is implemented using Hash Table, the key provided to map is hashed
into indices of a hash table which is why the performance of data structure depends on the hash
function a lot but on average, the cost of search, insert, and delete from the hash table is O(1). 

Map in STL: Maps are associative containers that store elements in a mapped fashion. Each


element has a key value and a mapped value. No two mapped values can have the same key
values.
Vector in STL: Vector is the same as dynamic arrays with the ability to resize itself
automatically when an element is inserted or deleted, with their storage being handled
automatically by the container. Vector elements are placed in contiguous storage so that they can
be accessed and traversed using iterators.
Vector of Maps in STL: Vector of maps can be used to design complex and efficient data
structures.
In vectors, data is inserted at the end. Inserting at the end takes differential time, as sometimes
the array may need to be extended. Removing the last element takes only constant time because
no resizing happens. Inserting and erasing at the beginning or in the middle is linear in time.
1. begin() – Returns an iterator pointing to the first element in the vector
2. end() – Returns an iterator pointing to the theoretical element that follows the last
element in the vector

3
3. rbegin() – Returns a reverse iterator pointing to the last element in the vector (reverse
beginning). It moves from last to first element
4. rend() – Returns a reverse iterator pointing to the theoretical element preceding the
first element in the vector (considered as reverse end)
5. cbegin() – Returns a constant iterator pointing to the first element in the vector.
6. cend() – Returns a constant iterator pointing to the theoretical element that follows
the last element in the vector.
7. crbegin() – Returns a const reverse iterator pointing to the last element in the vector
(reverse beginning). It moves from last to first element
8. crend() – Returns a const reverse iterator pointing to the theoretical element preceding
the first element in the vector (considered as reverse end)

A function is a set of statements that take inputs, do some specific computation, and produce
output. The idea is to put some commonly or repeatedly done tasks together and make
a function so that instead of writing the same code again and again for different inputs, we can
call the function.
In simple terms, a function is a block of code that only runs when it is called.

What is Recursion? 

The process in which a function calls itself directly or indirectly is called recursion and the
corresponding function is called a recursive function. Using a recursive algorithm, certain
problems can be solved quite easily. Examples of such problems are Towers of Hanoi
(TOH), Inorder/Preorder/Postorder Tree Traversals, DFS of Graph, etc. A recursive function
solves a particular problem by calling a copy of itself and solving smaller subproblems of the
original problems. Many more recursive calls can be generated as and when required. It is
essential to know that we should provide a certain case in order to terminate this recursion
process. So we can say that every time the function calls itself with a simpler version of the
original problem.

Need of Recursion
Recursion is an amazing technique with the help of which we can reduce the length of our code
and make it easier to read and write. It has certain advantages over the iteration technique which
will be discussed later. A task that can be defined with its similar subtask, recursion is one of the
best solutions for it. For example; The Factorial of a number.
Properties of Recursion:
● Performing the same operations multiple times with different inputs.
● In every step, we try smaller inputs to make the problem smaller.
● Base condition is needed to stop the recursion otherwise infinite loop will occur.

Priority queues are a type of container adapters, specifically designed such that the first element
of the queue is either the greatest or the smallest of all elements in the queue and elements are in
nonincreasing order (hence we can see that each element of the queue has a priority {fixed
order}). However in C++ STL, by default, the top element is always the greatest element. We

4
can also change it to the smallest element at the top. Priority queues are built on the top to the
max heap and uses array or vector as an internal structure.

Pair is used to combine together two values that may be of different data types. Pair provides a
way to store two heterogeneous objects as a single unit. It is basically used if we want to store
tuples. The pair container is a simple container defined in <utility> header consisting of two data
elements or objects. 
● The first element is referenced as ‘first’ and the second element as ‘second’ and the
order is fixed (first, second).
● Pair can be assigned, copied, and compared. The array of objects allocated in
a map or hash_map is of type ‘pair’ by default in which all the ‘first’ elements are
unique keys associated with their ‘second’ value objects.
● To access the elements, we use a variable name followed by a dot operator followed
by the keyword first or second.

Code:

#include<bits/stdc++.h>
#include<iostream>
#include <fstream>

using namespace std;

#define NO_CLASS = 6;
#define NO_LABS_CSE = 3;
#define NO_LABS_ECE = 3;

class Faculty;
class Batch;
class Subjects;
class Room;

unordered_map<Batch*, vector<Batch*>> batch_map;


vector<Batch*>  batches;
vector<Faculty*> faculties;
vector<Subjects*> subjects;
vector<Room*> rooms;
ofstream myfile;

5
Fig 1.1 Importing Libraries and Defining

We have imported all the required libraries like iostream.h, fstream.h and defined the classes we are using
such as Faculty, Batch, Subjects and Room and all the vectors we need for the classes.

class Room{
    public:
        int room_no;
        // 0 = class, 1 = lab
        int type;
        // 0 = CSE, 1 = ECE
        string name;
        int dept;

        Subjects* slots[5][8] = {NULL};


        Room(int room_no, string name, int type, int dept){
            this->room_no = room_no;
            this->type = type;
            this->name = name;
            this->dept = dept;
    }
        bool is_available_theory(int day, int slot){
            if(this->slots[day][slot] == NULL){
                return true;
      }
            return false;
    }
        bool is_available_lab(int day, int slot){
            if(this->slots[day][slot] == NULL && this->slots[day][slot+1] == NULL){
                return true;
      }
            return false;
    }
        bool is_available(int day, int slot, int type){
            if(type == 0){
                return is_available_theory(day, slot);
      }
            else{
                return is_available_lab(day, slot);

6
      }
    }
        bool assign_room_theory(Subjects* subject, int day, int slot){
            if(this->slots[day][slot] == NULL){
                this->slots[day][slot] = subject;
                return true;
      }
            return false;
    }
        bool assign_room_lab(Subjects* subject, int day, int slot){
            if(this->slots[day][slot] == NULL && this->slots[day][slot+1] == NULL){
                this->slots[day][slot] = subject;
                this->slots[day][slot+1] = subject;
                return true;
      }
            return false;
    }
        bool asign_room(Subjects* subject, int day, int slot, int type){
            if(type == 0){
                return assign_room_theory(subject, day, slot);
      }
            else{
                return assign_room_lab(subject, day, slot);
      }
    }

        void unassign_room_theory(int day, int slot){


            this->slots[day][slot] = NULL;
    }
        void unassign_room_lab(int day, int slot){
            this->slots[day][slot] = NULL;
            this->slots[day][slot+1] = NULL;
    }
        void unassign_room(int day, int slot, int type){
            if(type == 0){
                unassign_room_theory(day, slot);
      }
            else{
                unassign_room_lab(day, slot);
      }

7
    }
};

Fig 1.2 Room Class

This is a class for the assignment of rooms. In this, we have taken into consideration the number of rooms
available for theory and labs and we accordingly assign and unassign the rooms for them.

class Subjects{
    public:
        int id;
        string name;
        set<Faculty *> faculties;
        //0 = CSE, 1 = ECE
        int dept;
        //0 = theory, 1 = lab
        int type;
        Batch* batches;
        int total_class;
        int class_count;
        Subjects(int id, string name, int dept, int type, int total_class){
            this->id = id;
            this->name = name;
            this->dept = dept;
            this->type = type;
            this->total_class = total_class;
            this->class_count = 0;
    }
        void addFaculty(Faculty* faculty){
            this->faculties.insert(faculty);
    }
        void removeFaculty(Faculty* faculty){
            this->faculties.erase(faculty);
    }
};

Fig 1.3 Subject Class

This is a class for assignment of classes and faculties. Here, we assign the subject details like name,
department, total classes etc. and we add and remove faculties accordingly.

8
class Faculty{
    public:
        int id;
        string name;
        string dept;
        set<Subjects *> subjects;
        Subjects* slots[5][8] = {NULL};

        Faculty(int id, string  name, string dept){


            this->id = id;
            this->name=  name;
            this->dept = dept;
    }
        void add_subject(Subjects *subject){
            this->subjects.insert(subject);
    }

        void remove_subject(Subjects *subject){


            this->subjects.erase(subject);
    }

        bool is_available_theory(int day, int slot_no){


            if(this->slots[day][slot_no] == NULL){
                    return true;
      }
            return false;
    }
        bool is_available_lab(int day, int slot_no){
            if(slot_no < 7 && this->slots[day][slot_no] == NULL && this->slots[day][slot_no+1]
== NULL){
                    return true;
      }
            return false;
    }
        bool is_available(int day, int slot_no, int type){
            if(type == 0){
                return is_available_theory(day, slot_no);
      }

9
            else{
                return is_available_lab(day, slot_no);
      }
    }
        bool add_slot_theory(Subjects* subject,int day, int slot){
            if(this->is_available_theory(day, slot)){
                this->slots[day][slot] = subject;
                return true;
      }
            return false;
    }
        bool add_slot_lab(Subjects* subject,int day, int slot){
            if(this->is_available_lab(day, slot)){
                this->slots[day][slot] = subject;
                this->slots[day][slot+1] = subject;
                return true;
      }
            return false;
    }
        bool add_slot(Subjects* subject,int day, int slot, int type){
            if(type == 0){
                return add_slot_theory(subject, day, slot);
      }
            else{
                return add_slot_lab(subject, day, slot);
      }
    }
        void remove_slot_theory(int day, int slot){
            this->slots[day][slot] = NULL;
    }
        void remove_slot_lab(int day, int slot){
            this->slots[day][slot] = NULL;
            this->slots[day][slot+1] = NULL;
    }

        void remove_slot(int day, int slot, int type){


            if(type == 0){
                remove_slot_theory(day, slot);
      }
            else{

10
                remove_slot_lab(day, slot);
      }
    }
};

Fig 1.4 Faculty Class


Here, we add the Faculty details first like the name of the faculty and department they belong to. We then
check the available slots for theory and lab and then accordingly we assign/remove a slot for the faculty.

class Batch{
    public:
        int id;
        string name;
        // 0= theory, 1 = lab
        int type;
        int year;
        set<Subjects*> subjects;
        Subjects* slots[5][8]={NULL};
        Batch(int id, string name, int type, int year){
            this->id = id;
            this->name = name;
            this->type = type;
            this->year = year;
    }
        void add_subject(Subjects *subject){
            this->subjects.insert(subject);
    }
        bool is_available_theory(int day, int slot_no){
            if(this->slots[day][slot_no] == NULL){
                return true;
      }
            return false;
    }
        bool is_available_lab(int day, int slot_no){

            if(slot_no < 7 && this->slots[day][slot_no] == NULL && this->slots[day][slot_no+1]


== NULL){
                return true;
      }

11
            return false;
    }
        bool is_available(int day, int slot_no, int type){     
            if(type == 0){
                return is_available_theory(day, slot_no);
      }
            else{
                return is_available_lab(day, slot_no);
      }
    }
        bool assign_theory(Subjects* subject, int day, int slot){   
                this->slots[day][slot] = subject;
                subject->class_count++;
                return true;
    }
        bool assign_lab(Subjects* subject, int day, int slot){  
                this->slots[day][slot] = subject;
                this->slots[day][slot+1] = subject;
                subject->class_count++;
                return true;
    }
        void assign(Subjects* subject, int day, int slot, int type){
            if(type == 0){
                assign_theory(subject, day, slot);
      }
            else{
                assign_lab(subject, day, slot);
      }
    }
        void remove_slot_theory(int day, int slot){
            this->slots[day][slot] = NULL;
    }
        void remove_slot_lab(int day, int slot){
            this->slots[day][slot] = NULL;
            this->slots[day][slot+1] = NULL;
    }
        void remove_slot(int day, int slot, int type){
            if(type == 0){
                remove_slot_theory(day, slot);
      }

12
            else{
                remove_slot_lab(day, slot);
      }
    }
};

Fig 1.5 Batch Class

We take the batch details like their name, which year they belong to etc. Then we check the available slots
for the subjects for the appropriate batches and then we assign/remove the slots for theory/lab for the
batch.

void create_subject(Batch* batch, vector<Faculty*> faculties, int id, string name, int dept, int
type, int total_class){
    Subjects* subject = new Subjects(id, name, dept, type, total_class);
    subjects.push_back(subject);
    subject->batches = batch;
    for(auto faculty: faculties){
        subject->addFaculty(faculty);
          faculty->add_subject(subject);
  }
    batch->add_subject(subject);
}

Fig 1.6 Creating Subject

This is a function for creating subjects. We create a subject for the batches and then we add a faculty for
that subject.

void init(){
    // init batches
    batches.push_back(new Batch(1, "CSE-4th-B1", 1, 4));
    batches.push_back(new Batch(2, "CSE-4th-B2", 1, 4));
    batches.push_back(new Batch(3, "ECE-4th-A1", 1, 4));
    batches.push_back(new Batch(4, "ECE-4th-A2", 1, 4));
    batches.push_back(new Batch(5, "CSE-4th", 0, 4));
    batches.push_back(new Batch(6, "ECE-4th", 0, 4));  
//init_batch_map
    batch_map[batches[0]].push_back(batches[4]);

13
    batch_map[batches[1]].push_back(batches[4]);
    batch_map[batches[2]].push_back(batches[5]);
    batch_map[batches[3]].push_back(batches[5]);
    // // init faculties
    faculties.push_back(new Faculty(1, "GA", "ECE"));
    faculties.push_back(new Faculty(2, "HG", "ECE"));
    faculties.push_back(new Faculty(3, "TD" , "ECE"));
    faculties.push_back(new Faculty(4, "SN", "ECE"));
    faculties.push_back(new Faculty(5, "SRS", "ECE"));
    // init subjects
//4th CSE
    create_subject(batches[4], {faculties[7]}, 1, "AI_theory_pk_cse", 0, 0, 1);
    create_subject(batches[4], {faculties[21]}, 2, "AI_theory_SP_cse", 0, 0, 2);
    //4th ECE
    create_subject(batches[5], {faculties[7]}, 16, "AI_theory_pk_ece", 1, 0, 1);
    create_subject(batches[5], {faculties[21]}, 17, "AI_theory_SP_ece", 1, 0, 2);
    // init rooms
    rooms.push_back(new Room(1, "C-1", 0, 0));
    rooms.push_back(new Room(2, "C-2", 0, 0));
    rooms.push_back(new Room(7, "CSE-L-1", 1, 0));
    rooms.push_back(new Room(10, "ECE-L-1", 1, 1));
 }

Fig 1.7 Initializing Data

This whole section of code is for initializing the data. We input the subject names, the faculty names, the
batches, and the rooms available and every single bit of detail required. This is a crucial step as without
the required data, we won't be able to create a timetable.

void print_table_head(){
    myfile << ",Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday\n";
}
vector<string> slot_timings = {"8.30-9.30", "9.30-10.30", "10.30-11.30", "11.30 - 12.30",
"2.00-3.00", "3.00-4.00", "4.00-5.00", "5.00-6.00"};
void print_slots(Subjects* slots[5][8], bool is_batch, Batch* batch){
    print_table_head();
    for(int i=0;i<8; i++){
        myfile << slot_timings[i] << ",";
        for(int j=0;j<5;j++){
            bool is_NULL = true;
            if(slots[j][i] != NULL){

14
                myfile << slots[j][i]->name << ";";
                is_NULL = false;
      }
            if(is_batch){
                for(auto b: batch_map[batch]){
                    if(b->slots[j][i] != NULL){
                        myfile <<"\t"<< b->slots[j][i]->name << ";";
                        is_NULL = false;
          }
        }
      }
            if(is_NULL){
                myfile << "-------------------";
      }
            myfile << ",";
    }
        myfile << "\n";
  }
}
void print_batch_time_table(){
    myfile << "Batches time table\n";
    for(int i=0;i<batches.size();i++){
        myfile << "Batch: " << batches[i]->name << "\n\n";
        print_slots(batches[i]->slots, true, batches[i]);
        myfile << "\n\n";
  }
}
void print_faculty_time_table(){
    myfile << "Faculty time table\n";
    for(int i=0;i<faculties.size();i++){
        myfile << "Faculty: " << faculties[i]->name << "\n\n";
        print_slots(faculties[i]->slots, false, NULL);
        myfile << "\n\n";
  }
}
void print_room_time_table(){
    myfile << "Room time table\n";
    for(int i=0;i<rooms.size();i++){
        myfile << "Room: " << rooms[i]->name << "\n\n";
        print_slots(rooms[i]->slots, false, NULL);

15
        myfile << "\n\n";
  }
}
static bool room_sort(Room* a, Room* b){
    return a->room_no < b->room_no;
}
void print_final_tables(){
    sort(rooms.begin(), rooms.end(), room_sort);
    print_batch_time_table();
    print_faculty_time_table();
    print_room_time_table();
}
int count_g = 0;
vector<int> days = {0,1,2,3,4};
vector<int> hours = {0,1,2,3,4,5,6,7};

Fig 1.8 Printing outputs and saving data on csv file

This is another crucial step as it displays the required timetables for the batches, the faculties, and the
rooms available. We use vectors to dynamically input the fields like printing slots for the subjects, etc. We
have created a 2D array for the slots of 5x8.

bool recursive(priority_queue<pair<int, Subjects*>, vector<pair<int, Subjects*>>,


greater<pair<int, Subjects*>>> pq){
    if(pq.empty()){
        return true;
  }
    // cout << "PQ size: " << pq.size() << endl;
    auto top = pq.top();
    pq.pop();
    auto subject = top.second;
    if(top.first > 1){
        pq.push({top.first - 1, subject});
  }
    random_shuffle(rooms.begin(), rooms.end());
    for(auto r: rooms){
        if(r->type==subject->type){
            if(r->type ==1 && r->dept != subject->dept){

16
                continue;
      }
            random_shuffle(days.begin(), days.end());
            random_shuffle(hours.begin(), hours.end());
            //iterate over slots
            for(int day=0;day<5;day++){
                for(int slot=0;slot<8;slot++){
                                        count_g++;
                    //check if room is available
                    if(r->is_available(days[day], hours[slot], subject->type)){
                        bool is_faculty_available = true;
                        for(auto f: subject->faculties){
                            if(!f->is_available(days[day], hours[slot], subject->type)){
                                is_faculty_available = false;
                                break;
              }
            }
                        // cout << "is_faculty_available: " << is_faculty_available << endl;
                        if(is_faculty_available){
                            //check if batch is available
                            if(subject->batches->is_available(days[day], hours[slot], subject->type)){
                                // cout << "Batch is available" << endl;
                                bool is_batch_available = true;
                                for(auto b: batch_map[subject->batches]){
                                    if(b->is_available(days[day], hours[slot], subject->type) == false){
                                        is_batch_available = false;
                                        break;
                  }
                }
                                if(is_batch_available){
                                    r->asign_room(subject,days[day], hours[slot], subject->type);
                                //block faculty
                                    for(auto f: subject->faculties){
                                        f->add_slot(subject,days[day], hours[slot], subject->type);
                  }
                                    //block batches
                                    subject->batches->assign(subject,days[day] ,hours[slot], subject->type);
                                    //recurse
                                    if(recursive(pq)){
                                        return true;

17
                  }
                                    //unblock batch
                                    subject->batches->remove_slot(days[day], hours[slot], subject->type);
                                    //unblock faculty
                                    for(auto f: subject->faculties){
                                        f->remove_slot(days[day], hours[slot], subject->type);
                  }
                                    //unblock room
                                    r->unassign_room(days[day], hours[slot], subject->type);
                  }
                                //block room
              }
            }
          }
        }
      }
    }
  }
    return false;
}

Fig 1.9 Recursive Function

We take the help of recursive functions to make our job easier and faster. We use priority queue for
determining which slots should be filled first and which shouldn’t be.

int main(){

    init();
    priority_queue<pair<int, Subjects*>, vector<pair<int, Subjects*>>, greater<pair<int,
Subjects*>>> pq;
    int n = subjects.size();
    for(int i=0;i<n;i++){
        pq.push({subjects[i]->total_class, subjects[i]});
  }
    if(recursive(pq)){   
        myfile.open ("output.csv");
        print_final_tables();
        myfile.close();
  }
    else{

18
        cout << "No solution found" << endl;
  }
    cout << "Count: " << count_g << endl;
    return 0;
}

Fig 2.0 Main Function

This is our main function where the execution of code begins and ends. We have called all the required
functions and we have executed the code and generated the timetable

OUTPUT:

CONCLUSION:

Thus, we have successfully created a fully automated timetable generator. This can be used to
generate any kind of timetable we want. All we need to do is change the data accordingly and
resize as and when necessary.

19

You might also like