Skip to content

Commit caa5424

Browse files
committed
Finished implementation for ActorGraph.cpp
1 parent 137148e commit caa5424

File tree

6 files changed

+356
-0
lines changed

6 files changed

+356
-0
lines changed

src/ActorGraph/ActorGraph.cpp

+206
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
#include "ActorGraph.hpp"
2+
3+
#include <fstream>
4+
#include <iostream>
5+
#include <queue>
6+
#include <sstream>
7+
#include <stack>
8+
#include <string>
9+
10+
#include "Node.hpp"
11+
12+
using namespace std;
13+
14+
// Initializing an empty graph
15+
ActorGraph::ActorGraph() { this->graph; }
16+
17+
/* Build the actor graph from dataset file.
18+
* Each line of the dataset file must be formatted as:
19+
* ActorName <tab> MovieName <tab> Year
20+
* Two actors are connected by an undirected edge if they have worked in a movie
21+
* before.
22+
*/
23+
bool ActorGraph::buildGraph(istream& is) {
24+
bool readHeader = false;
25+
unordered_map<string, vector<Node*>> movies;
26+
27+
while (is) {
28+
string s;
29+
if (!getline(is, s)) break;
30+
// skip the header of the file
31+
if (!readHeader) {
32+
readHeader = true;
33+
continue;
34+
}
35+
// read each line of the dataset to get the movie actor relation
36+
istringstream ss(s);
37+
vector<string> record;
38+
while (ss) {
39+
string str;
40+
if (!getline(ss, str, '\t')) break;
41+
record.push_back(str);
42+
}
43+
// if format is wrong, skip current line
44+
if (record.size() != 3) {
45+
continue;
46+
}
47+
// extract the information
48+
string actor(record[0]);
49+
string title(record[1]);
50+
int year = stoi(record[2]);
51+
// Append title with year
52+
string completeTitle = title + "#@" + record[2];
53+
// Helper u_map
54+
if (movies.find(completeTitle) == movies.end()) {
55+
Node* curr;
56+
if (this->graph.find(actor) == this->graph.end()) {
57+
curr = new Node(actor);
58+
} else {
59+
curr = this->graph.find(actor)->second;
60+
}
61+
62+
vector<Node*> list;
63+
list.push_back(curr);
64+
unordered_map<string, Node*> map;
65+
movies.insert({completeTitle, list});
66+
curr->outgoing.insert({completeTitle, map});
67+
// Found the movie title and year
68+
} else {
69+
vector<Node*> list = movies[completeTitle];
70+
// Check if actor is already in the list
71+
bool found = false;
72+
for (Node* curr : list) {
73+
if (curr->name == actor) {
74+
found = true;
75+
}
76+
}
77+
// Actor is not in the list
78+
if (!found) {
79+
Node* curr;
80+
if (this->graph.find(actor) == this->graph.end()) {
81+
curr = new Node(actor);
82+
} else {
83+
curr = this->graph.find(actor)->second;
84+
}
85+
86+
for (Node* tmp : list) {
87+
// If movie is not in this actor's node
88+
if (curr->outgoing.find(completeTitle) ==
89+
curr->outgoing.end()) {
90+
unordered_map<string, Node*> tmpMap;
91+
tmpMap.insert({tmp->name, tmp});
92+
curr->outgoing.insert({completeTitle, tmpMap});
93+
} else {
94+
curr->outgoing[completeTitle].insert({tmp->name, tmp});
95+
}
96+
curr->neighbors.push_back(tmp);
97+
tmp->outgoing[completeTitle].insert({curr->name, curr});
98+
tmp->neighbors.push_back(curr);
99+
}
100+
movies[completeTitle].push_back(curr);
101+
}
102+
}
103+
// If it does not exist
104+
if (this->graph.find(actor) == this->graph.end()) {
105+
// Check movie title
106+
if (movies.find(completeTitle) != movies.end()) {
107+
for (Node* curr : movies[completeTitle]) {
108+
if (curr->name == actor) {
109+
// insert into graph
110+
this->graph.insert({actor, curr});
111+
}
112+
}
113+
}
114+
}
115+
}
116+
// if failed to read the file, clear the graph and return
117+
if (!is.eof()) {
118+
this->~ActorGraph();
119+
return false;
120+
}
121+
return true;
122+
}
123+
124+
/* TODO */
125+
void ActorGraph::BFS(const string& fromActor, const string& toActor,
126+
string& shortestPath) {
127+
// If the actors don't exist
128+
if (this->graph.find(toActor) == this->graph.end() ||
129+
this->graph.find(fromActor) == this->graph.end()) {
130+
shortestPath = "";
131+
return;
132+
}
133+
// reset distance and visited
134+
for (pair<string, Node*> it : graph) {
135+
it.second->distance = 0;
136+
it.second->visited = false;
137+
}
138+
// initialize empty queue
139+
queue<pair<int, Node*>> path;
140+
// push the current node with zero distance
141+
pair<int, Node*> root(0, this->graph[fromActor]);
142+
path.push(root);
143+
while (!path.empty()) {
144+
int distance = path.front().first;
145+
Node* curr = path.front().second;
146+
path.pop();
147+
if (!curr->visited) {
148+
curr->visited = true;
149+
curr->distance = distance;
150+
// Loopping through neighbors
151+
for (auto nodes : curr->neighbors) {
152+
if (!nodes->visited) {
153+
path.push({distance + 1, nodes});
154+
}
155+
}
156+
}
157+
}
158+
// Finding the shortest path
159+
stack<string> shortest;
160+
Node* curr = this->graph[toActor];
161+
int isBroken = false;
162+
// if not the first actor yet
163+
while (curr->name != fromActor) {
164+
if (curr->distance == 0 && curr->name != fromActor) {
165+
shortestPath = "";
166+
return;
167+
}
168+
// loop through movies
169+
for (auto movies : curr->outgoing) {
170+
// loop through coworkers
171+
for (auto cw : movies.second) {
172+
if (cw.second->distance == curr->distance - 1) {
173+
// push to stack
174+
shortest.push(movies.first);
175+
shortest.push(cw.first);
176+
curr = cw.second;
177+
178+
// break
179+
isBroken = true;
180+
break;
181+
}
182+
}
183+
184+
if (isBroken) {
185+
isBroken = false;
186+
break;
187+
}
188+
}
189+
}
190+
// Building the shortest path
191+
while (!shortest.empty()) {
192+
string tmp = "(" + shortest.top() + ")";
193+
shortest.pop();
194+
tmp += "--[" + shortest.top() + "]-->";
195+
shortest.pop();
196+
shortestPath += tmp;
197+
}
198+
shortestPath += "(" + toActor + ")";
199+
}
200+
201+
// Destroy the list
202+
ActorGraph::~ActorGraph() {
203+
for (auto it = this->graph.begin(); it != this->graph.end(); it++) {
204+
delete (it->second);
205+
}
206+
}

src/ActorGraph/ActorGraph.hpp

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef ACTORGRAPH_HPP
2+
#define ACTORGRAPH_HPP
3+
4+
#include <iostream>
5+
#include <unordered_map>
6+
#include <vector>
7+
8+
#include "Node.hpp"
9+
10+
using namespace std;
11+
12+
class ActorGraph {
13+
protected:
14+
// Title, Node*
15+
unordered_map<string, Node*> graph;
16+
17+
public:
18+
/* TODO */
19+
ActorGraph();
20+
21+
/* TODO */
22+
bool buildGraph(istream& is);
23+
24+
/* TODO */
25+
void BFS(const string& fromActor, const string& toActor,
26+
string& shortestPath);
27+
28+
/* TODO */
29+
~ActorGraph();
30+
31+
// Check if a path exists
32+
// bool pathExists(string from, string to, string movie);
33+
};
34+
35+
#endif // ACTORGRAPH_HPP

src/ActorGraph/CMakeLists.txt

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
add_library (actorgraph_dep ActorGraph.cpp)
2+
target_include_directories(actorgraph_dep PUBLIC .)

src/ActorGraph/Node.hpp

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#ifndef NODE_HPP
2+
#define NODE_HPP
3+
#include <string>
4+
#include <unordered_map>
5+
6+
using namespace std;
7+
8+
class Node {
9+
public:
10+
string name; // Actor name
11+
int distance; // Distance
12+
unordered_map<string, unordered_map<string, Node*>> outgoing; // Edges
13+
bool visited; // BFS
14+
15+
Node(string name) : name(name), visited(false) { this->distance = 0; }
16+
};
17+
#endif

src/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
add_subdirectory(ActorGraph)
2+
3+
add_executable (pathfinder.exe pathfinder.cpp)
4+
target_link_libraries(pathfinder.exe PRIVATE actorgraph_dep)

src/pathfinder.cpp

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/**
2+
* CSE 100 PA4 Pathfinder in Actor Graph
3+
*/
4+
#include <cstdlib>
5+
#include <cstring>
6+
#include <fstream>
7+
#include <iostream>
8+
#include <sstream>
9+
#include <string>
10+
#include <vector>
11+
#include "ActorGraph.hpp"
12+
13+
using namespace std;
14+
15+
/* Print the usage of the program */
16+
void usage(char* program_name) {
17+
cerr << program_name << " called with incorrect arguments." << endl;
18+
cerr << "Usage: " << program_name
19+
<< " movie_cast_file actor_pairs_file shortest_paths_file" << endl;
20+
}
21+
22+
/* Main program that drives the pathfinder */
23+
int main(int argc, char* argv[]) {
24+
const int NUM_ARG = 4, INPUT_IDX = 1, PAIR_IDX = 2, OUT_IDX = 3,
25+
PAIR_SIZE = 2;
26+
27+
if (argc != NUM_ARG) {
28+
usage(argv[0]);
29+
return 1;
30+
}
31+
32+
char* graphFileName = argv[INPUT_IDX];
33+
char* pairs = argv[PAIR_IDX];
34+
char* output = argv[OUT_IDX];
35+
36+
// build the actor graph from the input file
37+
ActorGraph* graph = new ActorGraph();
38+
cout << "Reading " << graphFileName << " ..." << endl;
39+
ifstream graphFile(graphFileName);
40+
if (!graph->buildGraph(graphFile)) {
41+
cerr << "Failed to read " << graphFileName << endl;
42+
return 1;
43+
}
44+
graphFile.close();
45+
cout << "Done." << endl;
46+
47+
// write the shorest path of each given pair to the output file
48+
ifstream infile(pairs);
49+
ofstream outfile(output);
50+
bool haveHeader = false;
51+
52+
while (infile) {
53+
string s;
54+
if (!getline(infile, s)) break;
55+
56+
// skip reading the header in inFile and output the header in outFile
57+
if (!haveHeader) {
58+
outfile << "(actor)--[movie#@year]-->(actor)--..." << endl;
59+
haveHeader = true;
60+
continue;
61+
}
62+
63+
// read the pair from each line
64+
istringstream ss(s);
65+
vector<string> actorPair;
66+
while (ss) {
67+
string str;
68+
if (!getline(ss, str, '\t')) break;
69+
actorPair.push_back(str);
70+
}
71+
72+
// skip the incorrectly formatted line in input file
73+
if (actorPair.size() != PAIR_SIZE) {
74+
continue;
75+
}
76+
77+
string actor1(actorPair[0]);
78+
string actor2(actorPair[1]);
79+
80+
// output the shorest path for each line
81+
string shortestPath = "";
82+
graph->BFS(actor1, actor2, shortestPath);
83+
if (shortestPath.length() > 0) {
84+
outfile << shortestPath;
85+
}
86+
outfile << endl;
87+
}
88+
outfile.close();
89+
infile.close();
90+
delete graph;
91+
return 0;
92+
}

0 commit comments

Comments
 (0)