/* Fear Pathing generation utility. (c) 2005 Father Nitwit */ #include "../common/types.h" #include "../zone/map.h" #include "../common/rdtsc.h" #include "quadtree.h" #include "apathing.h" #include "boostcrap.h" #include #include #include #include #include /* We assume this overwrites the edge array in big and does not free the old edges */ void build_boost_graph(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, bool set_weights) { list::iterator cur,end; list::iterator curn,endn; PathNode *n; PathEdge *e; //first we need to number our nodes... curn = big->nodes.begin(); endn = big->nodes.end(); int r = 0; for(; curn != endn; curn++) { n = *curn; n->node_id = r; r++; } typedef std::pair < int, int > E; //our edge type weightmap = get(edge_weight, vg); //now add all our edges to the graph cur = big->edges.begin(); end = big->edges.end(); for(r = 0; cur != end; cur++, r++) { e = *cur; if(e->from == e->to) continue; // printf("A %d/%d (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f) d=%.3f\n", r, big->edges.size(), e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z, e->from->Dist2(e->to)); EdgeDesc ed; bool inserted; tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg); if(set_weights) weightmap[ed] = int(e->from->Dist2(e->to)); else weightmap[ed] = 1; em[e] = ed; // e->edge_id = ed; } //now we should have a nice happy undirected graph... } void run_min_spanning_tree(MyGraph &vg, property_map::type &weightmap, map &em, PathGraph *big, int start_node) { int noedge = 0; list out_paths; vector < EdgeDesc > spanning_tree; kruskal_minimum_spanning_tree(vg, back_inserter(spanning_tree)); vector < EdgeDesc >::iterator cur,end; list::iterator cure,ende; cur = spanning_tree.begin(); end = spanning_tree.end(); for(; cur != end; cur++) { //find the edge cure = big->edges.begin(); ende = big->edges.end(); for(; cure != ende; cure++) { if(em[*cure] == *cur) { out_paths.push_back(new PathEdge((*cure)->from, (*cure)->to)); break; } } } noedge = big->edges.size() - out_paths.size() - 1; printf("Ran Min Spanning Tree: %d paths were eliminated.\n", noedge); /* //make our results list. vector < VertDesc > p(num_vertices(vg)); //finally run the stupid algorithm. prim_minimum_spanning_tree(vg, &p[0], root_vertex(start_node)); for (std::size_t i = 0; i != p.size(); ++i) { if (p[i] != i) { out_paths.push_back(new PathEdge(big->nodes[p[i]], big->nodes[i])); } else { //no edge here... noedge++; } } printf("Ran Min Spanning Tree: %d nodes were disconnected.\n", noedge); */ //now swap out our edge list to the MST. big->edges = out_paths; } void find_disjoint_grids(Map *map, MyGraph &vg, PathGraph *big, const char *fname, vector &start_nodes, vector &disjoints) { vector< vector > D; vector counts; vector disjoint_counts; vector first_node; //color the graph and get us the info we need to do out job color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); //find the biggest grid, that one gets to be included no matter what int best_graph = 0; int best_count = counts[0]; unsigned int r; for(r = 1; r < counts.size(); r++) { if(best_count < counts[r]) { best_count = counts[r]; best_graph = r; } } disjoint_counts[best_graph] = 1; //break up the graphs based on color if we want them. vector::iterator ccur,djcur,fcur,cend; list::iterator cur,end; PathEdge *e; PathGraph *pg; ccur = counts.begin(); djcur = disjoint_counts.begin(); fcur = first_node.begin(); cend = counts.end(); int color = 0; for(; ccur != cend; ccur++, djcur++, fcur++, color++) { int count = *ccur; int dj = *djcur; // int fn = *fcur; if(dj < 1) continue; //skip disjoint sets not marked for use. if(count < MIN_DISJOINT_NODES) continue; //make sure we have a reasonable node count pg = new PathGraph(); cur = big->edges.begin(); end = big->edges.end(); for(; cur != end; cur++) { e = *cur; if(e->from->color != e->to->color) { printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z); //... what to do... } //just use the color of the from node if(e->from->color == color) { pg->edges.push_back(e); } } //get our list of nodes based on our edge list. rebuild_node_list(pg->edges, pg->nodes, NULL); disjoints.push_back(pg); start_nodes.push_back(1); //each graph only contains its own nodes, so any node will work. } /* int best_graph = 0; int best_count = counts[0]; unsigned int r; for(r = 1; r < counts.size(); r++) { if(best_count < counts[r]) { best_count = counts[r]; best_graph = r; } // printf("Graph %d has %d edges\n", r, counts[r]); } start_node = first_node[best_graph]; printf("Best sub-graph: chose #%d of %d, has %d nodes, and contains node %d\n", best_graph, counts.size()-1, best_count, start_node); //todo: eliminate other graphs... */ /* list::iterator cure,ende; PathEdge *e; cure = big->edges.begin(); ende = big->edges.end(); for(; cure != ende; cure++) { e = *cure; if(e->from->color != e->to->color) { printf("Color Mismatch %d-%d: #%d(%.3f,%.3f,%.3f) -> #%d(%.3f,%.3f,%.3f)\n", e->from->color, e->to->color, e->from->node_id, e->from->x, e->from->y, e->from->z, e->to->node_id, e->to->x, e->to->y, e->to->z); } else if(e->from->color != best_graph) { printf("Color %d: (%.3f,%.3f,%.3f) -> (%.3f,%.3f,%.3f)\n", e->from->color, e->from->x, e->from->y, e->from->z, e->to->x, e->to->y, e->to->z); } } */ } static const int INT_LIMIT = (std::numeric_limits < int >::max)(); void color_disjoint_graphs( PathGraph *big, MyGraph &vg, Map *map, const char *fname, vector< vector > &D, //output vector &counts, //output vector &disjoint_counts, //output vector &first_node //output ) { int count = big->nodes.size(); // int r; // vector< vector > D(count, vector(count, INT_LIMIT)); // vector counts(1, 0); // vector first_node(1, 0); //make sure everything is inited right... D.resize(0); D.resize(count, vector(count, INT_LIMIT)); counts.resize(1); counts[0] = 0; disjoint_counts.resize(1); disjoint_counts[0] = 0; first_node.resize(1); first_node[0] = 0; //make up a weight map with all 1s, done while building now /* property_map < MyGraph, edge_weight_t >::type w = get(edge_weight, vg); graph_traits < MyGraph >::edge_iterator e, e_end; for (boost::tie(e, e_end) = edges(vg); e != e_end; ++e) w[*e] = 1;*/ johnson_all_pairs_shortest_paths(vg, D); list::iterator cur,end,cur2; PathNode *n,*f; int cur_color = 1; int cc,djc; //clear node colors cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; n->color = 0; } //color the graph based on reachability, basically labeling subgraphs //this finds the number of nodes reachable from each node //which is used to pick the best disconnected graph in the tree. //its a series of wrong bullshit that forces us to do this, but it works cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; if(n->color != 0) continue; //allready visited //printf("New Color at: (%.3f,%.3f,%.3f)\n", n->x, n->y, n->z); cc = 1; djc = 0; if(n->disjoint) djc++; n->color = cur_color; cur_color++; cur2 = cur; for(; cur2 != end; cur2++) { f = *cur2; if(f->color == 0 && D[n->node_id][f->node_id] != INT_LIMIT) { cc++; f->color = n->color; } if(f->disjoint) djc++; } counts.push_back(cc); disjoint_counts.push_back(djc); first_node.push_back(n->node_id); } #ifdef DRAW_ALL_COLORS if(fname != NULL) { printf("Drawing with %d seperate sub-graphs from %d nodes and %d edges\n", cur_color-1, big->nodes.size(), big->edges.size()); FILE *pngout; pngout = fopen(fname, "wb"); if(pngout == NULL) { printf("Unable to open %s\n", fname); return; } gdImagePtr im; int minx = int(map->GetMinX()); int maxx = int(map->GetMaxX()); int miny = int(map->GetMinY()); int maxy = int(map->GetMaxY()); im = gdImageCreate((maxx - minx)/IMAGE_SCALE, (maxy - miny)/IMAGE_SCALE); // im = gdImageCreate(maxx - minx, maxy - miny); //allocate this first, to make it the BG color. /*int black =*/ gdImageColorAllocate(im, 0, 0, 0); // int grey = gdImageColorAllocate(im, 100, 100, 100); int *clist = new int[cur_color]; int r; for(r = 0; r < cur_color; r++) { clist[r] = gdImageColorAllocate(im, rand()%255, rand()%255, rand()%255); } { list::iterator cur,end; PathEdge *e; cur = big->edges.begin(); end = big->edges.end(); for(; cur != end; cur++) { e = *cur; int x1 = int(e->from->x) - minx; int y1 = int(e->from->y) - miny; int x2 = int(e->to->x) - minx; int y2 = int(e->to->y) - miny; x1 /= IMAGE_SCALE; y1 /= IMAGE_SCALE; x2 /= IMAGE_SCALE; y2 /= IMAGE_SCALE; gdImageLine(im, x1, y1, x2, y2, clist[e->from->color]); } } delete[] clist; gdImagePng(im, pngout); gdImageDestroy(im); fclose(pngout); printf("Wrote image: %s\n", fname); } #endif //DRAW_ALL_COLORS } void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, map &em, const char *fname) { vector counts; vector disjoint_counts; vector first_node; vector< vector > D; list::iterator cur,end; PathNode *n; /* Node distances: 1. find the root node. - find the longest path from each node to any other node - the node with the shortest of these longest paths is the root 2. record each node's distance from that root node */ //color the graph and get us the info we need to do out job color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); vector::iterator curp,endp; int shortest_node = 0; int shortest = 0xFFFFFF; int the_longest = 0; int longest_node = 0; //stores the longest path from each node vector longest_dists(D.size(), 0); //find the node with the longest path of all, so we know what //tree we are trying to find the root of cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; vector &cv = D[n->node_id]; curp = cv.begin(); endp = cv.end(); int longest = 0; int discount = 0; for(; curp != endp; curp++) { if(*curp == INT_LIMIT) { discount++; continue; } if(*curp > longest) longest = *curp; } longest_dists[n->node_id] = longest; if(longest > the_longest) { the_longest = longest; longest_node = n->node_id; } } //find the node with the shortest, longest path //the idea is to locate the 'root' of the tree. cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; vector &cv = D[n->node_id]; if(cv[longest_node] == INT_LIMIT) continue; //this node cannot reach the root int longest = longest_dists[n->node_id]; //n->longest_path = longest; //printf("Node %d's longest path is %d\n", n->node_id, longest); if(longest < shortest) { shortest = longest; shortest_node = n->node_id; } } //now we have our root, set each node to their distance from the root vector &root_dists = D[shortest_node]; printf("The tree's root is %d\n", shortest_node); cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; if(n->node_id == shortest_node) n->longest_path = 0; else n->longest_path = root_dists[n->node_id]; //printf("Node %d's distance from root is %d\n", n->node_id, root_dists[n->node_id]); } /* the reachability is the number of nodes which we could possibly get to by traveling each direction across an edge. to find reachability: loop through each edge e Remove that edge from the graph color_disjoint_graphs fc = count number of nodes with the same color as 'e->from' tc = count number of nodes with the same color as 'e->to' e->normal_reach = tc; e->reverse_reach = fc; */ property_map::type weightmap; weightmap = get(edge_weight, vg); int tc,fc; int from_color, to_color; printf("Finding lengths (%d dots)", 1+big->edges.size()/10); int pos = 0; PathEdge *e; list::iterator cur4,end4; cur4 = big->edges.begin(); end4 = big->edges.end(); for(; cur4 != end4; cur4++, pos++) { if(pos % 10 == 0) { printf("."); fflush(stdout); } e = *cur4; //remove this edge from the boost graph.. remove_edge(em[e], vg); //color the graph and get us the info we need to do our job //char out[64]; //sprintf(out, "lengraph-%d.png", pos); color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node); //add the edge back in EdgeDesc ed; bool inserted; tie(ed, inserted) = add_edge(e->from->node_id, e->to->node_id, vg); em[e] = ed; weightmap[em[e]] = int(e->from->Dist2(e->to)); //cause its a new edge //make sure this stupid thing worked. if(e->from->color == e->to->color) { printf("Cycle detected in MST... WTF?\n"); e->normal_reach = -1; e->reverse_reach = -1; continue; } from_color = e->from->color; to_color = e->to->color; //count our crap. tc = 0; fc = 0; cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; if(n->color == to_color) tc++; else if(n->color == from_color) fc++; } //put it on our edge e->normal_reach = tc; e->reverse_reach = fc; } printf("\n"); //re-color the graph, since we dicked with it above color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); choose_biggest_graph(big, counts, first_node); } void just_color_the_damned_thing(Map *map, PathGraph *big, const char *fname) { MyGraph vg(big->nodes.size()); property_map::type weightlist_mst; std::map edgemap_mst; build_boost_graph(vg, weightlist_mst, edgemap_mst, big, true); vector counts; vector disjoint_counts; vector first_node; vector< vector > D; list::iterator cur,end; color_disjoint_graphs(big, vg, map, fname, D, counts, disjoint_counts, first_node); } /* void calc_path_lengths(Map *map, MyGraph &vg, PathGraph *big, vector< vector > &D, const char *fname) { // vector< vector > D; vector counts; vector first_node; choose_biggest_graph(big, counts, first_node); } */ //assumes that the graph is freshly colored disjoint, and counts/first_node are results from it void choose_biggest_graph(PathGraph *big, vector &counts, vector &first_node) { int best_graph = 0; int best_count = counts[0]; unsigned int r; for(r = 1; r < counts.size(); r++) { if(best_count < counts[r]) { best_count = counts[r]; best_graph = r; } printf("Graph %d has %d edges\n", r, counts[r]); } printf("Best Tree: Detected %d graphs in the MST, selected #%d\n", counts.size(), best_graph); list new_nodes; list new_edges; //rebuild the node list to include only nodes which are in //the best graph, also only keeping the edges which connect them. std::map havenodelist; list::iterator cur4,end4; cur4 = big->edges.begin(); end4 = big->edges.end(); for(; cur4 != end4; cur4++) { PathEdge *e = *cur4; //remove the edge if it is not the main color //this also causes the removal of the nodes if they //are not used in any other edges. if(e->from->color != e->to->color) { printf("Miscolor.\n"); continue; } if(e->from->color != best_graph) { continue; } if(e->to->color != best_graph) { continue; } new_edges.push_back(e); if(havenodelist.count(e->from) != 1) { e->from->final_id = new_nodes.size(); new_nodes.push_back(e->from); havenodelist[e->from] = 1; } if(havenodelist.count(e->to) != 1) { e->to->final_id = new_nodes.size(); new_nodes.push_back(e->to); havenodelist[e->to] = 1; } } printf("Best Tree: Removed %d nodes and %d edges which were disconnected.\n", big->nodes.size() - new_nodes.size(), big->edges.size() - new_edges.size()); big->nodes = new_nodes; big->edges = new_edges; } void consolidate_cross_graphs(Map *map, PathGraph *big, PathGraph *excess, MyGraph &cross_graph, const char *fname) { vector< vector > D; vector counts; vector disjoint_counts; vector first_node; //color the graph and get us the info we need to do our job color_disjoint_graphs(big, cross_graph, map, fname, D, counts, disjoint_counts, first_node); } void find_path_info(Map *map, MyGraph &vg, vector< vector > &path_finding, PathGraph *big) { //make sure our path finding vector is big enough. { int size = big->nodes.size(); vector tmp(size, (PathEdge*)NULL); path_finding.resize(0); path_finding.resize(size, tmp); } vector< vector > D; vector counts; vector disjoint_counts; vector first_node; //color the graph and get us the info we need to do our job color_disjoint_graphs(big, vg, map, NULL, D, counts, disjoint_counts, first_node); //figure out what edges link to each node. std::map > node_edges; find_node_edges(big, node_edges); //for each node, find best edge to reach each other node. list::iterator cur,end; PathNode *n; vector::iterator curp,endp; vector::iterator cure,ende; int r; cur = big->nodes.begin(); end = big->nodes.end(); for(; cur != end; cur++) { n = *cur; //get all our vector refs that we need since this is kinda expensive vector &cv = D[n->node_id]; //distance array vector &pf = path_finding[n->node_id]; //result vector &el = node_edges[n]; //our edges curp = cv.begin(); endp = cv.end(); for(r = 0; curp != endp; curp++, r++) { if(n->node_id == r) { //this is the end of the path, we cannot take an edge to reach ourself pf[r] = NULL; continue; } int my_dist = *curp; if(my_dist == INT_MAX) { //this node is unreachable. pf[r] = NULL; continue; } //else, node r reachable from us, find best edge. cure = el.begin(); ende = el.end(); // int shortest; PathEdge *ce; PathNode *cn; //for each edge for(; cure != ende; cure++) { ce = *cure; //find the node other than ourself on the edge if(ce->from == n) cn = ce->to; else cn = ce->from; //see how far away this node is int cdist = D[cn->node_id][r]; if(cdist < my_dist) { //found one which is closer... due to min span tree, there //should only be one path possible so just go with it. pf[r] = ce; break; } } //assume that this node got assigned.. next if(pf[r] == NULL) { printf("Node id %d was not able to find a good path to node %d\n", n->node_id, r); } } } }