Exemple #1
0
/// Wegfinden ( A* ),  O(v lg v) --> Wegfindung auf allgemeinen Terrain (ohne Straßen),  für Wegbau und frei herumlaufende Berufe
bool GameWorldBase::FindFreePathAlternatingConditions(const MapPoint start, 
                                 const MapPoint dest,  const bool random_route, 
                                 const unsigned max_route,  std::vector<unsigned char>* route,  unsigned* length, 
                                 unsigned char* first_dir,   FP_Node_OK_Callback IsNodeOK,  FP_Node_OK_Callback IsNodeOKAlternate,  FP_Node_OK_Callback IsNodeToDestOk,  const void* param,  const bool record) const
{
    // increase currentVisit,  so we don't have to clear the visited-states at every run
    currentVisit++;
	//currentVisitEven++;

    // if the counter reaches its maxium,  tidy up
    if (currentVisit == std::numeric_limits<unsigned>::max() - 1)
    {
        for (unsigned i = 0; i < (maxMapSize * maxMapSize); ++i)
        {
            pf_nodes[i].lastVisited = 0;
			pf_nodes[i].lastVisitedEven = 0;
        }
        currentVisit = 1;
		//currentVisitEven = 1;
    }

    std::list<PathfindingPoint> todo;
	bool prevstepEven=true; //flips between even and odd 
	unsigned stepsTilSwitch=1;
    PathfindingPoint::Init(dest,  this);

    // Anfangsknoten einfügen
    unsigned start_id = MakeCoordID(start);
	todo.push_back(PathfindingPoint(start,  start_id));
    // Und mit entsprechenden Werten füllen
    //pf_nodes[start_id].it_p = ret.first;
    pf_nodes[start_id].prevEven = INVALID_PREV;
    pf_nodes[start_id].lastVisitedEven = currentVisit;
    pf_nodes[start_id].wayEven = 0;
    pf_nodes[start_id].dirEven = 0;
	//LOG.lprintf("pf: from %i, %i to %i, %i \n", x_start, y_start, x_dest, y_dest);
    // TODO confirm random
    unsigned rand = GetIdx(start) * GAMECLIENT.GetGFNumber() % 6; //RANDOM.Rand(__FILE__,  __LINE__,  y_start * GetWidth() + x_start,  6);

	while(!todo.empty())
    {		
		if(!stepsTilSwitch) //counter for next step and switch condition
		{			
			prevstepEven=!prevstepEven;
			stepsTilSwitch=todo.size();
			//prevstepEven? LOG.lprintf("pf: even,  to switch %i listsize %i ", stepsTilSwitch, todo.size()) : LOG.lprintf("pf: odd,  to switch %i listsize %i ", stepsTilSwitch, todo.size());
		}
		//else
			//prevstepEven? LOG.lprintf("pf: even,  to switch %i listsize %i ", stepsTilSwitch, todo.size()) : LOG.lprintf("pf: odd,  to switch %i listsize %i ", stepsTilSwitch, todo.size());
		stepsTilSwitch--;

        // Knoten mit den geringsten Wegkosten auswählen
        PathfindingPoint best = *todo.begin();
        // Knoten behandelt --> raus aus der todo Liste
        todo.erase(todo.begin());

        //printf("x: %u y: %u\n", best.x, best.y);

        // ID des besten Punktes ausrechnen

        unsigned best_id = best.id;
		//LOG.lprintf(" now %i, %i id: %i \n", best.x, best.y, best_id);
        // Dieser Knoten wurde aus dem set entfernt,  daher wird der entsprechende Iterator
        // auf das Ende (also nicht definiert) gesetzt,  quasi als "NULL"-Ersatz
        //pf_nodes[best_id].it_p = todo.end();

        // Ziel schon erreicht? Allerdings Null-Weg,  wenn Start=Ende ist,  verbieten
        if(dest == best.pt && ((prevstepEven && pf_nodes[best_id].wayEven) || (!prevstepEven && pf_nodes[best_id].way)))
        {
            // Ziel erreicht!
            // Jeweils die einzelnen Angaben zurückgeben,  falls gewünscht (Pointer übergeben)
            if(length)
				*length = prevstepEven ? pf_nodes[best_id].wayEven : pf_nodes[best_id].way;
            if(route)
                prevstepEven? route->resize(pf_nodes[best_id].wayEven) : route->resize(pf_nodes[best_id].way);

            // Route rekonstruieren und ggf. die erste Richtung speichern,  falls gewünscht
			bool alternate=prevstepEven;
            for(unsigned z = prevstepEven? pf_nodes[best_id].wayEven - 1 : pf_nodes[best_id].way - 1; best_id != start_id; --z,  best_id = alternate? pf_nodes[best_id].prevEven : pf_nodes[best_id].prev,  alternate=!alternate)
            {
                if(route)
                    (*route)[z] = alternate? pf_nodes[best_id].dirEven : pf_nodes[best_id].dir;
                if(first_dir && z == 0)
                    *first_dir = pf_nodes[best_id].dirEven;				
            }

            // Fertig,  es wurde ein Pfad gefunden
            return true;
        }

        // Maximaler Weg schon erreicht? In dem Fall brauchen wir keine weiteren Knoten von diesem aus bilden
        if((prevstepEven && pf_nodes[best_id].wayEven)==max_route || (!prevstepEven && pf_nodes[best_id].way == max_route))
            continue;

        // Bei Zufälliger Richtung anfangen (damit man nicht immer denselben Weg geht,  besonders für die Soldaten wichtig)
        unsigned startDir = random_route ? rand : 0;
		//LOG.lprintf("pf get neighbor nodes %i, %i id: %i \n", best.x, best.y, best_id);
        // Knoten in alle 6 Richtungen bilden
        for(unsigned z = startDir + 3; z < startDir + 9; ++z)
        {
            unsigned i = z % 6;

            // Koordinaten des entsprechenden umliegenden Punktes bilden
            MapPoint na = GetNeighbour(best.pt,  i);

            // ID des umliegenden Knotens bilden
            unsigned xaid = MakeCoordID(na);

            // Knoten schon auf dem Feld gebildet?
            if ((prevstepEven && pf_nodes[xaid].lastVisited == currentVisit) || (!prevstepEven && pf_nodes[xaid].lastVisitedEven == currentVisit))
            {
                continue;
            }

            // Das Ziel wollen wir auf jedenfall erreichen lassen,  daher nur diese zusätzlichen
            // Bedingungen,  wenn es nicht das Ziel ist
            if(na != dest && ((prevstepEven && IsNodeOK) || (!prevstepEven && IsNodeOKAlternate)))
            {
                if(prevstepEven)
				{
					if(!IsNodeOK(*this,  na,  i,  param))
						continue;
				}
				else
				{
					if (!IsNodeOKAlternate(*this,  na,  i,  param))
						continue;
					MapPoint p = best.pt;

					std::vector<MapPoint>evenlocationsonroute;
					bool alternate=prevstepEven;
					unsigned back_id=best_id;
					for(unsigned i=pf_nodes[best_id].way-1; i>1; i--, back_id = alternate? pf_nodes[back_id].prevEven : pf_nodes[back_id].prev,  alternate=!alternate) // backtrack the plannend route and check if another "even" position is too close
					{
						unsigned char pdir = alternate? pf_nodes[back_id].dirEven : pf_nodes[back_id].dir;
						p = GetNeighbour(p,  (pdir+3)%6);
						if(i%2==0) //even step
						{	
							evenlocationsonroute.push_back(p);
						}
					}
					bool tooclose=false;
					//LOG.lprintf("pf from %i, %i to %i, %i now %i, %i ", x_start, y_start, x_dest, y_dest, xa, ya);//\n
					for(std::vector<MapPoint>::const_iterator it=evenlocationsonroute.begin();it!=evenlocationsonroute.end(); ++it)
					{
						//LOG.lprintf("dist to %i, %i ", temp, *it);
						if(CalcDistance(na,  (*it))<2)
						{
							tooclose=true;
							break;
						}
					}
					//LOG.lprintf("\n");
					if(CalcDistance(na,  start)<2)
						continue;
					if(CalcDistance(na,  dest)<2)
						continue;
					if(tooclose)
						continue;
				}
            }

            // Zusätzliche Bedingungen,  auch die das letzte Stück zum Ziel betreffen
            if(IsNodeToDestOk)
            {
                if(!IsNodeToDestOk(*this,  na,  i,  param))
                    continue;
            }

            // Alles in Ordnung,  Knoten kann gebildet werden
            prevstepEven? pf_nodes[xaid].lastVisited = currentVisit			: pf_nodes[xaid].lastVisitedEven = currentVisit;
            prevstepEven? pf_nodes[xaid].way = pf_nodes[best_id].wayEven + 1: pf_nodes[xaid].wayEven = pf_nodes[best_id].way + 1;
            prevstepEven? pf_nodes[xaid].dir = i							: pf_nodes[xaid].dirEven = i;
            prevstepEven? pf_nodes[xaid].prev = best_id						: pf_nodes[xaid].prevEven = best_id	;

            todo.push_back(PathfindingPoint(na,  xaid));
            //pf_nodes[xaid].it_p = ret.first;
        }
    }

    // Liste leer und kein Ziel erreicht --> kein Weg
    return false;
}
/// Pathfinder ( A* ), O(v lg v) --> Normal terrain (ignoring roads) for road building and free walking jobs
bool FreePathFinder::FindPathAlternatingConditions(const MapPoint start, const MapPoint dest,
                                                   const bool randomRoute, const unsigned maxLength,
                                                   std::vector<unsigned char>* route, unsigned* length, unsigned char* firstDir,
                                                   FP_Node_OK_Callback IsNodeOK, FP_Node_OK_Callback IsNodeOKAlternate, FP_Node_OK_Callback IsNodeToDestOk, const void* param)
{
    if(start == dest)
    {
        // Path where start==goal should never happen
        RTTR_Assert(false);
        LOG.write("WARNING: Bug detected (GF: %u). Please report this with the savegame and replay (Start==Dest in pathfinding %u,%u)\n") % gwb_.GetEvMgr().GetCurrentGF() % unsigned(start.x) % unsigned(start.y);
        // But for now we assume it to be valid and return (kind of) correct values
        if(route)
            route->clear();
        if(length)
            *length = 0;
        if(firstDir)
            *firstDir = 0xff;
        return true;
    }

    // increase currentVisit, so we don't have to clear the visited-states at every run
    IncreaseCurrentVisit();

    std::list<PathfindingPoint> todo;
    const unsigned destId = gwb_.GetIdx(dest);

    bool prevStepEven = true; //flips between even and odd 
    unsigned stepsTilSwitch = 1;

    // Add start node
    unsigned startId = gwb_.GetIdx(start);
    todo.push_back(PathfindingPoint(startId, gwb_.CalcDistance(start, dest), 0));
    // And init it
    nodes[startId].prevEven = INVALID_PREV;
    nodes[startId].lastVisitedEven = currentVisit;
    nodes[startId].wayEven = 0;
    nodes[startId].dirEven = 0;
    //LOG.write(("pf: from %i, %i to %i, %i \n", x_start, y_start, x_dest, y_dest);

    // Start at random dir (so different jobs may use different roads)
    const unsigned startDir = randomRoute ? (gwb_.GetIdx(start)) * gwb_.GetEvMgr().GetCurrentGF() % 6 : 0;

    while(!todo.empty())
    {		
        if(!stepsTilSwitch) //counter for next step and switch condition
        {			
            prevStepEven = !prevStepEven;
            stepsTilSwitch = todo.size();
            //prevstepEven ? LOG.write(("pf: even, to switch %i listsize %i ", stepsTilSwitch, todo.size()) : LOG.write(("pf: odd, to switch %i listsize %i ", stepsTilSwitch, todo.size());
        }
        //else
        //prevstepEven ? LOG.write(("pf: even, to switch %i listsize %i ", stepsTilSwitch, todo.size()) : LOG.write(("pf: odd, to switch %i listsize %i ", stepsTilSwitch, todo.size());
        stepsTilSwitch--;

        // Get node with lowest cost
        PathfindingPoint best = *todo.begin();
        // Knoten behandelt --> raus aus der todo Liste
        todo.erase(todo.begin());

        //printf("x: %u y: %u\n", best.x, best.y);

        // ID des besten Punktes ausrechnen

        unsigned bestId = best.id_;
        //LOG.write((" now %i, %i id: %i \n", best.x, best.y, best_id);
        // Dieser Knoten wurde aus dem set entfernt, daher wird der entsprechende Iterator
        // auf das Ende (also nicht definiert) gesetzt, quasi als "NULL"-Ersatz
        //pf_nodes[best_id].it_p = todo.end();

        // Ziel schon erreicht?
        if(destId == bestId)
        {
            // Ziel erreicht!
            // Return the values if requested
            const unsigned routeLen = prevStepEven ? nodes[bestId].wayEven : nodes[bestId].way;
            if(length)
                *length = routeLen;
            if(route)
                route->resize(routeLen);

            // Reconstruct route and get first direction (if requested)
            bool alternate = prevStepEven;
            for(unsigned z = routeLen - 1; bestId != startId; --z)
            {
                if(route)
                    (*route)[z] = alternate ? nodes[bestId].dirEven : nodes[bestId].dir;
                if(firstDir && z == 0)
                    *firstDir = nodes[bestId].dirEven;

                bestId = alternate ? nodes[bestId].prevEven : nodes[bestId].prev;
                alternate = !alternate;
            }

            // Fertig, es wurde ein Pfad gefunden
            return true;
        }

        // Maximaler Weg schon erreicht ? In dem Fall brauchen wir keine weiteren Knoten von diesem aus bilden
        if((prevStepEven && nodes[bestId].wayEven == maxLength) || (!prevStepEven && nodes[bestId].way == maxLength))
            continue;

        //LOG.write(("pf get neighbor nodes %i, %i id: %i \n", best.x, best.y, best_id);
        // Knoten in alle 6 Richtungen bilden
        for(unsigned z = startDir + 3; z < startDir + 9; ++z)
        {
            unsigned i = z % 6;

            // Koordinaten des entsprechenden umliegenden Punktes bilden
            MapPoint neighbourPos = gwb_.GetNeighbour(nodes[bestId].mapPt, i);

            // ID des umliegenden Knotens bilden
            unsigned nbId = gwb_.GetIdx(neighbourPos);

            // Knoten schon auf dem Feld gebildet ?
            if ((prevStepEven && nodes[nbId].lastVisited == currentVisit) || (!prevStepEven && nodes[nbId].lastVisitedEven == currentVisit))
            {
                continue;
            }

            // Check additional constraints for non-destination points
            if(nbId != destId && ((prevStepEven && IsNodeOK) || (!prevStepEven && IsNodeOKAlternate)))
            {
                if(prevStepEven)
                {
                    if(!IsNodeOK(gwb_, neighbourPos, i, param))
                        continue;
                }
                else
                {
                    if (!IsNodeOKAlternate(gwb_, neighbourPos, i, param))
                        continue;
                    MapPoint p = nodes[bestId].mapPt;

                    std::vector<MapPoint> evenLocationsOnRoute;
                    bool alternate = prevStepEven;
                    unsigned back_id = bestId;
                    for(unsigned i = nodes[bestId].way-1; i>1; i--) // backtrack the plannend route and check if another "even" position is too close
                    {
                        unsigned char pdir = alternate ? nodes[back_id].dirEven : nodes[back_id].dir;
                        p = gwb_.GetNeighbour(p, (pdir+3) % 6);
                        if(i%2 == 0) //even step
                        {	
                            evenLocationsOnRoute.push_back(p);
                        }
                        back_id = alternate ? nodes[back_id].prevEven : nodes[back_id].prev;
                        alternate = !alternate;
                    }
                    bool tooClose = false;
                    //LOG.write(("pf from %i, %i to %i, %i now %i, %i ", x_start, y_start, x_dest, y_dest, xa, ya);//\n
                    for(std::vector<MapPoint>::const_iterator it = evenLocationsOnRoute.begin();it!= evenLocationsOnRoute.end(); ++it)
                    {
                        //LOG.write(("dist to %i, %i ", temp, *it);
                        if(gwb_.CalcDistance(neighbourPos, (*it)) < 2)
                        {
                            tooClose = true;
                            break;
                        }
                    }
                    //LOG.write(("\n");
                    if(tooClose)
                        continue;
                    if(gwb_.CalcDistance(neighbourPos, start) < 2)
                        continue;
                    if(gwb_.CalcDistance(neighbourPos, dest) < 2)
                        continue;
                }
            }

            // Conditions for all nodes
            if(IsNodeToDestOk)
            {
                if(!IsNodeToDestOk(gwb_, neighbourPos, i, param))
                    continue;
            }

            // Alles in Ordnung, Knoten kann gebildet werden
            unsigned way;
            if(prevStepEven)
            {
                nodes[nbId].lastVisited = currentVisit;
                way = nodes[nbId].way = nodes[bestId].wayEven + 1;
                nodes[nbId].dir = i;
                nodes[nbId].prev = bestId;
            }
            else
            {
                nodes[nbId].lastVisitedEven = currentVisit;
                way = nodes[nbId].wayEven = nodes[bestId].way + 1;
                nodes[nbId].dirEven = i;
                nodes[nbId].prevEven = bestId;
            }

            todo.push_back(PathfindingPoint(nbId, gwb_.CalcDistance(neighbourPos, dest), way));
            //pf_nodes[xaid].it_p = ret.first;
        }
    }

    // Liste leer und kein Ziel erreicht --> kein Weg
    return false;
}
Exemple #3
0
/// Wegfinden ( A* ),  O(v lg v) --> Wegfindung auf allgemeinen Terrain (ohne Straßen),  für Wegbau und frei herumlaufende Berufe
bool GameWorldBase::FindFreePath(const MapPoint start, 
                                 const MapPoint dest,  const bool random_route, 
                                 const unsigned max_route,  std::vector<unsigned char>* route,  unsigned* length, 
                                 unsigned char* first_dir,   FP_Node_OK_Callback IsNodeOK,  FP_Node_OK_Callback IsNodeToDestOk,  const void* param,  const bool record) const
{
    // increase currentVisit,  so we don't have to clear the visited-states at every run
    currentVisit++;

    // if the counter reaches its maxium,  tidy up
    if (currentVisit == std::numeric_limits<unsigned>::max() - 1)
    {
        for (unsigned i = 0; i < (maxMapSize * maxMapSize); ++i)
        {
            pf_nodes[i].lastVisited = 0;
			pf_nodes[i].lastVisitedEven = 0;
        }
        currentVisit = 1;
    }

    std::set<PathfindingPoint> todo;
    PathfindingPoint::Init(dest,  this);

    // Anfangsknoten einfügen
    unsigned start_id = MakeCoordID(start);
    std::pair< std::set<PathfindingPoint>::iterator,  bool > ret = todo.insert(PathfindingPoint(start,  start_id));
    // Und mit entsprechenden Werten füllen
    pf_nodes[start_id].it_p = ret.first;
    pf_nodes[start_id].prev = INVALID_PREV;
    pf_nodes[start_id].lastVisited = currentVisit;
    pf_nodes[start_id].way = 0;
    pf_nodes[start_id].dir = 0;

    // TODO confirm random
    unsigned rand = (GetIdx(start)) * GAMECLIENT.GetGFNumber() % 6; //RANDOM.Rand(__FILE__,  __LINE__,  y_start * GetWidth() + x_start,  6);

    while(!todo.empty())
    {
        // Knoten mit den geringsten Wegkosten auswählen
        PathfindingPoint best = *todo.begin();
        // Knoten behandelt --> raus aus der todo Liste
        todo.erase(todo.begin());

        //printf("x: %u y: %u\n", best.x, best.y);

        // ID des besten Punktes ausrechnen

        unsigned best_id = best.id;

        // Dieser Knoten wurde aus dem set entfernt,  daher wird der entsprechende Iterator
        // auf das Ende (also nicht definiert) gesetzt,  quasi als "NULL"-Ersatz
        pf_nodes[best_id].it_p = todo.end();

        // Ziel schon erreicht? Allerdings Null-Weg,  wenn Start=Ende ist,  verbieten
        if(dest == best.pt && pf_nodes[best_id].way)
        {
            // Ziel erreicht!
            // Jeweils die einzelnen Angaben zurückgeben,  falls gewünscht (Pointer übergeben)
            if(length)
                *length = pf_nodes[best_id].way;
            if(route)
                route->resize(pf_nodes[best_id].way);

            // Route rekonstruieren und ggf. die erste Richtung speichern,  falls gewünscht
            for(unsigned z = pf_nodes[best_id].way - 1; best_id != start_id; --z,  best_id = pf_nodes[best_id].prev)
            {
                if(route)
                    (*route)[z] = pf_nodes[best_id].dir;
                if(first_dir && z == 0)
                    *first_dir = pf_nodes[best_id].dir;
            }

            // Fertig,  es wurde ein Pfad gefunden
            return true;
        }

        // Maximaler Weg schon erreicht? In dem Fall brauchen wir keine weiteren Knoten von diesem aus bilden
        if(pf_nodes[best_id].way == max_route)
            continue;

        // Bei Zufälliger Richtung anfangen (damit man nicht immer denselben Weg geht,  besonders für die Soldaten wichtig)
        unsigned start = random_route ? rand : 0;

        // Knoten in alle 6 Richtungen bilden
        for(unsigned z = start + 3; z < start + 9; ++z)
        {
            unsigned i = z % 6;

            // Koordinaten des entsprechenden umliegenden Punktes bilden
            MapPoint na = GetNeighbour(best.pt,  i);

            // ID des umliegenden Knotens bilden
            unsigned xaid = MakeCoordID(na);

            // Knoten schon auf dem Feld gebildet?
            if (pf_nodes[xaid].lastVisited == currentVisit)
            {
                // Dann nur ggf. Weg und Vorgänger korrigieren,  falls der Weg kürzer ist
                if(pf_nodes[xaid].it_p != todo.end() && pf_nodes[best_id].way + 1 < pf_nodes[xaid].way)
                {
                    pf_nodes[xaid].way  = pf_nodes[best_id].way + 1;
                    pf_nodes[xaid].prev = best_id;
                    todo.erase(pf_nodes[xaid].it_p);
                    ret = todo.insert(PathfindingPoint(na,  xaid));
                    pf_nodes[xaid].it_p = ret.first;
                    pf_nodes[xaid].dir = i;
                }
                // Wir wollen nicht denselben Knoten noch einmal einfügen,  daher Abbruch
                continue;
            }

            // Das Ziel wollen wir auf jedenfall erreichen lassen,  daher nur diese zusätzlichen
            // Bedingungen,  wenn es nicht das Ziel ist
            if(na != dest && IsNodeOK)
            {
                if(!IsNodeOK(*this,  na,  i,  param))
                    continue;
            }

            // Zusätzliche Bedingungen,  auch die das letzte Stück zum Ziel betreffen
            if(IsNodeToDestOk)
            {
                if(!IsNodeToDestOk(*this,  na,  i,  param))
                    continue;
            }

            // Alles in Ordnung,  Knoten kann gebildet werden
            pf_nodes[xaid].lastVisited = currentVisit;
            pf_nodes[xaid].way = pf_nodes[best_id].way + 1;
            pf_nodes[xaid].dir = i;
            pf_nodes[xaid].prev = best_id;

            ret = todo.insert(PathfindingPoint(na,  xaid));
            pf_nodes[xaid].it_p = ret.first;
        }
    }

    // Liste leer und kein Ziel erreicht --> kein Weg
    return false;
}