0

Sorry for the code dump

The code is being typed into a command line.

Graph.cpp

#ifndef GRAPH_CPP
#define GRAPH_CPP
#include <map>
#include <set>
#include <queue>
#include "Graph.h"
#include <iostream>

using namespace std;

template <typename T>
Graph<T>::Graph(){

}
//check if an edge exists between v and w
template <typename T>
bool Graph<T>::hasEdge(T v, T w) const
{
if (adjMap.find(v) == adjMap.end())
return false;
if (adjMap.at(v).find(w) != adjMap.at(v).end())
return true;
else
return false;
}

template <typename T>
void Graph<T>::addEdge(T v, T w) {
adjMap[v].insert(w);
adjMap[w].insert(v);
}

template <typename T>
int Graph<T>::BFS(T s, T t, map<T,int>& distance, map<T,T>& go_through) const
{
if (adjMap.find(s) == adjMap.end() || adjMap.find(t) == adjMap.end())
{
cout << "Invalid source vertex or/and destination vertex!" << endl;
return INVALID_VERTEX;
}
//Mark all the vertices with current distance to s: -1
for (auto it = adjMap.begin(); it != adjMap.end(); it++) {
distance[it->first] = NOPATH;
}

//Create queue for bfs
queue<T> bfs;
bfs.push(s);
//mark distance from source s to s:0
distance[s] = 0;
//mark shortest path to given source s going through s
go_through[s] = s;
T current = bfs.front();
while (!bfs.empty() && current != t) {
current = bfs.front();
set<T> adjVertices = adjMap.at(current);
for (auto i = adjVertices.begin(); i != adjVertices.end(); i++) {
if (distance[*i] == NOPATH) {
distance[*i] = distance[current] + 1;
go_through[*i] = current;
bfs.push(*i);
}
}
bfs.pop();
}
return distance[t];

}

#endif

Graph.h

#ifndef GRAPH_H
#define GRAPH_H

#include <map>
#include <set>
 
using namespace std;
 
const int NOPATH = -1;
const int INVALID_VERTEX = -2;

template <class T>
class Graph
{
   public:
    // default constructor
    Graph();
    
    // to check if an edge exists between v and w
    bool hasEdge(T v, T w) const;  
    
    // to add an edge beween v and w to the graph
    void addEdge(T v, T w);

    // Apply BFS traversal to find the shortest path from the given source s to destination t
    // return the distnace from s to t
    // if s or t does not exist, return INVALID_VERTEX (=-2) 
    // if there is no path from s to t, return NOPATH (=-1)
    // store the shortest path distance from the given source s  to vertex w in distance map<w, distance>
    // store which next vertex to go through on the shortest path to the given source s in go_through map<w, v>. 
    // Here a pair <w, v> in go_through map represents on the shortest path from w to the given source s, it needs to take the path: w-->v...-->s  
    int BFS(T s, T t, map<T, int>& distance, map<T, T>& go_through) const;
   
   private:    
    //represent the mapping from a Vertex, say u (key) to a set of vertices (value) which directly connect to u 
    map<T, set<T> > adjMap;        
};

#endif   /* GRAPH_H */

PA3Part2.cpp

#include <vector>
#include <iostream>
#include <string>
#include <fstream>
#include <stack>
#include <algorithm>     // for transform() function
#include "Graph.h"

using namespace std;

// declare global variable (constant only)
const int ARGUMENTS = 2;   // define the required command line arguments

// define a function to decide the collection of edges for a graph
// using "Word Buckets Approach"
Graph<string> WordBuckets_addEdges(const vector<string>& words);

int main(int argc, char* argv[])
{
    // Check whether the number of command line arguments match the required one 
    if (argc != ARGUMENTS)
    {
        cout << "Warning: need exactly " << ARGUMENTS-1 << " command line argument." << endl;
        cout << "Usage: " << argv[0] << " inputfile_name" << endl;
        return 1;
    }

    ifstream in_file;
    in_file.open(argv[1]);
    // Check whether the input file can be open successfully or not
    if (!in_file.good())
    {
        cout << "Warning: cannot open file named " << argv[1] << "!" << endl;
        return 2;
    }
    
    // Read data from the input file, assume "good data" from the input file
    // each line of the input file constains one word from English dictionary
    string word;
    vector<string> words;

    while (in_file >> word)
    {
        // convert word to all lower-case letters
        // if assume that each word from the input file is in lower-case letters, this step can be ignored
        transform(word.begin(), word.end(), word.begin(), ::tolower);
        words.push_back(word);
    }
    // close the input file
    in_file.close();

    // build the graph based on the collection of words
    // each word in the collection represents a Vertex in the graph
    // there is an edge from one word to another if these two words are only different by a single letter
    Graph<string> wordsG = WordBuckets_addEdges(words);

    string word1, word2;

    while (true)
    {
        cout << endl << endl;
        cout << "Welcome to CS216 Word Ladder!" << endl;
        cout << "Please give me two English words, and I will convert the first into the second by modifying one letter at a time." << endl;
        cout << "For the Demo purpose, let us try four-letter words only." << endl;
        cout << "Please type the FIRST four-letter word (or type Enter to quit): " << endl;
        getline(cin, word1);
        
        if (word1 == "")
            break;
        
        cout << "Please type the SECOND four-letter word (or type Enter to quit): " << endl;
        getline(cin, word2);
        
        if (word2 == "")
            break;

        // convert both word1 and word2 to all lower-case letters
        transform(word1.begin(), word1.end(), word1.begin(), ::tolower);
        transform(word2.begin(), word2.end(), word2.begin(), ::tolower);

        map<string,int> distance;
            map<string, string> go_through;
        int dis = wordsG.BFS(word1, word2, distance, go_through);
        // Display the ladder distance from word1 to word2
        // Display the shortest path on the word ladder from word1 to word2
        // the ladder distance is defined as the number of edges on the shortest path from one vertex to another
        // the ladder distance of the same vertex to itself is 0 
        // if word1 and word2 are the same word, your program should display "Two words should be different." 
        // there is no path from word1 to word2 if its ladder distance is -1
        // there is no path from word1 to word2 if its ladder distance is -2, which means either word1 or/and word2 is invalid
        if ( dis == NOPATH || dis == INVALID_VERTEX)
            cout << "There is no path from [" << word1 << "] to [" << word2 << "]" << endl;
        else if ( dis == 0)
        {
            cout << "Two words should be different." << endl;
        }
        else
        {
            cout << "The ladder distance from [" << word1 << "] to [" << word2 << "] is " << dis << "-step away." << endl;
            cout << "A ladder from [" << word1 << "] to [" << word2 << "]:" << endl;
        string nextVertex = word2;
            cout << word2;
            while (go_through[nextVertex] != word1)
            {
                cout << " ---> " << go_through[nextVertex];
                nextVertex = go_through[nextVertex];
            }
            cout << " ---> " << word1 << endl;
        }
    }

    cout << "Thank you for using this program, bye..." << endl;
    return 0;
}

/* define a function to decide the collection of edges for a graph
 * using "Word Buckets Approach"
 * Purpose: build a graph based on the collection of vertices
 *          each word stored in the vector, represents a Vertex in the graph
 *          there is an edge from one word to another 
 *          if these two words are only different by a single letter
 *@param words vector<string>: representing the collection of vertices, each string stored in the vector forms a vertex
 *@return Graph<string>: representing the graph built by
 *                       (1) the collection of vertices from words;
 *                       (2) the collection of edges are decided by if any two words are only different by a single letter
 *                         e.g. there is an edge between "cool" and "pool", but there is no edge between "cool" and "poll"
 *
 */
Graph<string> WordBuckets_addEdges(const vector<string>& words)
{    
    // build the graph
    // there is an edge from one word to another if the two words are only different by a single letter.
    // use "Word Bucket" solution described in Lab11 to efficiently decide whether an edge between two vertices
    Graph<string> wordsG;
    
    // provide your code here 
    // implement "Word Buckets Approach" to decide if you need to call addEdge() member function between two vertices
    map<string, vector<string> > bucket;
    for (int i = 0; i < words.size(); i++) {
        for (int j = 0; j < words[i].length(); j++) {
            string key = words[i];
            key[j] = '?';
            bucket[key].push_back(words[i]);
        }
    }

    for (auto k = bucket.begin(); k != bucket.end(); k++) {
        for (int s = 0; k->second.size(); s++) {
            for (int m = 0; m<k->second.size(); m++) {
                if (k->second[s] != k->second[m]) {
                    wordsG.addEdge(k->second[s],k->second[m]);
                }
            }
        }
    }
    return wordsG;
}

So after I completed the code and fixed all the errors, I tried compiling PA3Part2.cpp and Graph.cpp together into an executable program, which gave me this error below.

Error when attempting to compile:

/usr/bin/ld: /tmp/ccHsDrH9.o: in function main': PA3Part2.cpp:(.text+0x4ef): undefined reference to Graph<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >::BFS(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::map<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, int, std::less<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits, std::allocator > const, int> > >&, std::map<std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::less<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >, std::allocator<std::pair<std::__cxx11::basic_string<char, std::char_traits, std::allocator > const, std::__cxx11::basic_string<char, std::char_traits, std::allocator > > > >&) const' /usr/bin/ld: /tmp/ccHsDrH9.o: in function WordBuckets_addEdges(std::vector<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > > > const&)': PA3Part2.cpp:(.text+0x9f0): undefined reference to Graph<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >::Graph()' /usr/bin/ld: PA3Part2.cpp:(.text+0xcca): undefined reference to `Graph<std::__cxx11::basic_string<char, std::char_traits, std::allocator > >::addEdge(std::__cxx11::basic_string<char, std::char_traits, std::allocator >, std::__cxx11::basic_string<char, std::char_traits, std::allocator >)' collect2: error: ld returned 1 exit status

I'm stuck on what to do here, please help and thank you.

I recall having this problem before and not having as much trouble as I am with this.

  • In this case the fix should be pretty simple: Rename Graph.cpp to something your tools won't assume is a source file and compile like Graph.impl and `#include` it at the bottom of Graph.h. – user4581301 Dec 08 '22 at 04:06
  • It output an error. Graph.impl: file not recognized: file format not recognized. – ShadyPeach Dec 08 '22 at 04:14
  • Worked fine when I tried it. What are you using to compile the program? `#include` doesn't care what you feed it, it just mindlessly copies the contents of the inculded file into the including file. Odds are you have an IDE that's trying to be too smart for it's own good. Let us know what tool it is and the folks familiar with it can start pitching work-arounds. – user4581301 Dec 08 '22 at 04:21
  • I'm using a virtual machine provided by my course. – ShadyPeach Dec 08 '22 at 04:25
  • The virtual machine isn't what you're using to **compile** the program. – sweenish Dec 08 '22 at 04:25
  • Oh, I'm using a terminal. – ShadyPeach Dec 08 '22 at 04:26
  • If you are typing in a command line, please add it to the question. What you're tripping over is the *Rename Graph.cpp to something your tools won't assume is a source file and compile* part of the first comment. We need to know what tool you're using so we can name the file appropriately. If all else fails, merge Graph.cpp and Graph.h into Graph.h, but we should be able to find an innocuous name. Maybe Graph.impl.h. – user4581301 Dec 08 '22 at 04:31
  • You're getting closer. The terminal does not compile anything. You are issuing a command to an actual compiler, and that builds your code. You need to be able to separate these things. – sweenish Dec 08 '22 at 17:25
  • As frivolous example, iTerm2 is my terminal emulator, fish is my shell, I write my code in either Visual Studio Code or helix, and I build my C++ programs with clang++. That's skipping stuff like build systems and version control and maybe CI/CD pipelines. You don't have to know all of that. But we can start by being able to properly answer how you build your code. – sweenish Dec 08 '22 at 17:28

0 Answers0