Beispiel #1
0
/**
**  Assign workers to collect resources.
**
**  If we have a shortage of a resource, let many workers collecting this.
**  If no shortage, split workers to all resources.
*/
static void AiCollectResources()
{
    std::vector<CUnit *> units_assigned[MaxCosts]; // Worker assigned to resource
    std::vector<CUnit *> units_unassigned[MaxCosts]; // Unassigned workers
    int num_units_with_resource[MaxCosts];
    int num_units_assigned[MaxCosts];
    int num_units_unassigned[MaxCosts];
    int percent[MaxCosts];
    int priority_resource[MaxCosts];
    int priority_needed[MaxCosts];
    int wanted[MaxCosts];
    int total_harvester = 0;

    memset(num_units_with_resource, 0, sizeof(num_units_with_resource));
    memset(num_units_unassigned, 0, sizeof(num_units_unassigned));
    memset(num_units_assigned, 0, sizeof(num_units_assigned));

    // Collect statistics about the current assignment
    const int n = AiPlayer->Player->GetUnitCount();
    for (int i = 0; i < n; ++i) {
        CUnit &unit = AiPlayer->Player->GetUnit(i);
        if (!unit.Type->Harvester) {
            continue;
        }

        // See if it's assigned already
        if (unit.Orders.size() == 1 &&
                unit.CurrentAction() == UnitActionResource) {
            const COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder());
            const int c = order.GetCurrentResource();
            units_assigned[c].push_back(&unit);
            num_units_assigned[c]++;
            total_harvester++;
            continue;
        }

        // Ignore busy units. ( building, fighting, ... )
        if (!unit.IsIdle()) {
            continue;
        }

        // Send workers with resources back home.
        if (unit.ResourcesHeld) {
            const int c = unit.CurrentResource;

            num_units_with_resource[c]++;
            CommandReturnGoods(unit, 0, FlushCommands);
            total_harvester++;
            continue;
        }

        // Look what the unit can do
        for (int c = 1; c < MaxCosts; ++c) {
            if (unit.Type->ResInfo[c]) {
                units_unassigned[c].push_back(&unit);
                num_units_unassigned[c]++;
            }
        }
        ++total_harvester;
    }

    if (!total_harvester) {
        return;
    }

    memset(wanted, 0, sizeof(wanted));

    int percent_total = 100;
    for (int c = 1; c < MaxCosts; ++c) {
        percent[c] = AiPlayer->Collect[c];
        if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed
            percent_total += percent[c];
            percent[c] <<= 1;
        }
    }

    // Turn percent values into harvester numbers.
    for (int c = 1; c < MaxCosts; ++c) {
        if (percent[c]) {
            // Wanted needs to be representative.
            if (total_harvester < 5) {
                wanted[c] = 1 + (percent[c] * 5) / percent_total;
            } else {
                wanted[c] = 1 + (percent[c] * total_harvester) / percent_total;
            }
        }
    }

    // Initialise priority & mapping
    for (int c = 0; c < MaxCosts; ++c) {
        priority_resource[c] = c;
        priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c];

        if (c && num_units_assigned[c] > 1) {
            //first should go workers with lower ResourcesHeld value
            std::sort(units_assigned[c].begin(), units_assigned[c].end(), CmpWorkers);
        }
    }
    CUnit *unit;
    do {
        // sort resources by priority
        for (int i = 0; i < MaxCosts; ++i) {
            for (int j = i + 1; j < MaxCosts; ++j) {
                if (priority_needed[j] > priority_needed[i]) {
                    std::swap(priority_needed[i], priority_needed[j]);
                    std::swap(priority_resource[i], priority_resource[j]);
                }
            }
        }
        unit = NULL;

        // Try to complete each ressource in the priority order
        for (int i = 0; i < MaxCosts; ++i) {
            int c = priority_resource[i];

            // If there is a free worker for c, take it.
            if (num_units_unassigned[c]) {
                // Take the unit.
                while (0 < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][0], c)) {
                    // can't assign to c => remove from units_unassigned !
                    units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]];
                    units_unassigned[c].pop_back();
                }

                // unit is assigned
                if (0 < num_units_unassigned[c]) {
                    unit = units_unassigned[c][0];
                    units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]];
                    units_unassigned[c].pop_back();

                    // remove it from other ressources
                    for (int j = 0; j < MaxCosts; ++j) {
                        if (j == c || !unit->Type->ResInfo[j]) {
                            continue;
                        }
                        for (int k = 0; k < num_units_unassigned[j]; ++k) {
                            if (units_unassigned[j][k] == unit) {
                                units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]];
                                units_unassigned[j].pop_back();
                                break;
                            }
                        }
                    }
                }
            }

            // Else : Take from already assigned worker with lower priority.
            if (!unit) {
                // Take from lower priority only (i+1).
                for (int j = i + 1; j < MaxCosts && !unit; ++j) {
                    // Try to move worker from src_c to c
                    const int src_c = priority_resource[j];

                    // Don't complete with lower priority ones...
                    if (wanted[src_c] > wanted[c]
                            || (wanted[src_c] == wanted[c]
                                && num_units_assigned[src_c] <= num_units_assigned[c] + 1)) {
                        continue;
                    }

                    for (int k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) {
                        unit = units_assigned[src_c][k];

                        Assert(unit->CurrentAction() == UnitActionResource);
                        COrder_Resource &order = *static_cast<COrder_Resource *>(unit->CurrentOrder());

                        if (order.IsGatheringFinished()) {
                            //worker returning with resource
                            continue;
                        }

                        // unit can't harvest : next one
                        if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) {
                            unit = NULL;
                            continue;
                        }

                        // Remove from src_c
                        units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]];
                        units_assigned[src_c].pop_back();

                        // j need one more
                        priority_needed[j]++;
                    }
                }
            }

            // We just moved an unit. Adjust priority & retry
            if (unit) {
                // i got a new unit.
                priority_needed[i]--;
                // Recompute priority now
                break;
            }
        }
    } while (unit);

    // Unassigned units there can't be assigned ( ie : they can't move to ressource )
    // IDEA : use transporter here.
}
/**
**  Assign workers to collect resources.
**
**  If we have a shortage of a resource, let many workers collecting this.
**  If no shortage, split workers to all resources.
*/
static void AiCollectResources()
{
	CUnit *units_with_resource[MaxCosts][UnitMax]; // Worker with resource
	CUnit *units_assigned[MaxCosts][UnitMax]; // Worker assigned to resource
	CUnit *units_unassigned[MaxCosts][UnitMax]; // Unassigned workers
	int num_units_with_resource[MaxCosts];
	int num_units_assigned[MaxCosts];
	int num_units_unassigned[MaxCosts];
	int c;
	int src_c;
	int i;
	int j;
	int k;
	int n;
	CUnit **units;
	int percent[MaxCosts];
	int percent_total;

	int priority_resource[MaxCosts];
	int priority_needed[MaxCosts];
	int wanted[MaxCosts];
	int total_harvester;

	memset(num_units_with_resource, 0, sizeof(num_units_with_resource));
	memset(num_units_unassigned, 0, sizeof(num_units_unassigned));
	memset(num_units_assigned, 0, sizeof(num_units_assigned));

	//
	// Collect statistics about the current assignment
	//
	total_harvester = 0;

	n = AiPlayer->Player->TotalNumUnits;
	units = AiPlayer->Player->Units;
	for (i = 0; i < n; ++i) {
		CUnit &unit = *units[i];
		if (!unit.Type->Harvester) {
			continue;
		}

		c = unit.CurrentResource;

		//
		// See if it's assigned already
		//
		if (c && unit.OrderCount == 1 &&
			unit.CurrentAction() == UnitActionResource) {
			units_assigned[c][num_units_assigned[c]++] = &unit;
			total_harvester++;
			continue;
		}

		//
		// Ignore busy units. ( building, fighting, ... )
		//
		if (!unit.IsIdle()) {
			continue;
		}

		//
		// Send workers with resources back home.
		//
		if (unit.ResourcesHeld && c) {
			units_with_resource[c][num_units_with_resource[c]++] = &unit;
			CommandReturnGoods(unit, 0, FlushCommands);
			total_harvester++;
			continue;
		}

		//
		// Look what the unit can do
		//
		for (c = 1; c < MaxCosts; ++c) {
			if (unit.Type->ResInfo[c]) {
				units_unassigned[c][num_units_unassigned[c]++] = &unit;
			}
		}
		++total_harvester;
	}

	if(!total_harvester) {
		return;
	}

	memset(wanted, 0, sizeof(wanted));

	percent_total = 100;
	for (c = 1; c < MaxCosts; ++c) {
		percent[c] = AiPlayer->Collect[c];
		if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed
			percent_total += percent[c];
			percent[c] <<= 1;
		}
	}

	//
	// Turn percent values into harvester numbers.
	//
	for (c = 1; c < MaxCosts; ++c ) {
		if(percent[c]) {
			// Wanted needs to be representative.
			if (total_harvester < 5) {
				wanted[c] = 1 + (percent[c] * 5) / percent_total;
			} else {
				wanted[c] = 1 + (percent[c] * total_harvester) / percent_total;
			}
		}
	}

	//
	// Initialise priority & mapping
	//
	for (c = 0; c < MaxCosts; ++c) {
		priority_resource[c] = c;
		priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c];

		if (c && num_units_assigned[c] > 1) {
			//first should go workers with lower ResourcesHeld value
			qsort(units_assigned[c], num_units_assigned[c],
							 sizeof(CUnit*), CmpWorkers);
		}

	}
	CUnit* unit;
	do {
		//
		// sort resources by priority
		//
		for (i = 0; i < MaxCosts; ++i) {
			for (j = i + 1; j < MaxCosts; ++j) {
				if (priority_needed[j] > priority_needed[i]) {
					c = priority_needed[j];
					priority_needed[j] = priority_needed[i];
					priority_needed[i] = c;
					c = priority_resource[j];
					priority_resource[j] = priority_resource[i];
					priority_resource[i] = c;
				}
			}
		}
		unit = NoUnitP;
		//
		// Try to complete each ressource in the priority order
		//

		for (i = 0; i < MaxCosts; ++i) {
			c = priority_resource[i];

			//
			// If there is a free worker for c, take it.
			//
			if (num_units_unassigned[c]) {
				// Take the unit.
				j = 0;
				while (j < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][j], c)) {
					// can't assign to c => remove from units_unassigned !
					units_unassigned[c][j] = units_unassigned[c][--num_units_unassigned[c]];
				}

				// unit is assigned
				if (j < num_units_unassigned[c]) {
					unit = units_unassigned[c][j];
					units_unassigned[c][j] = units_unassigned[c][--num_units_unassigned[c]];

					// remove it from other ressources
					for (j = 0; j < MaxCosts; ++j) {
						if (j == c || !unit->Type->ResInfo[j]) {
							continue;
						}
						for (k = 0; k < num_units_unassigned[j]; ++k) {
							if (units_unassigned[j][k] == unit) {
								units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]];
								break;
							}
						}
					}
				}
			}

			//
			// Else : Take from already assigned worker with lower priority.
			//
			if (!unit) {
				// Take from lower priority only (i+1).
				for (j = i + 1; j < MaxCosts && !unit; ++j) {
					// Try to move worker from src_c to c
					src_c = priority_resource[j];

					// Don't complete with lower priority ones...
					if (wanted[src_c] > wanted[c] ||
							(wanted[src_c] == wanted[c] &&
								num_units_assigned[src_c] <= num_units_assigned[c] + 1)) {
						continue;
					}

					for (k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) {
						unit = units_assigned[src_c][k];

						if (unit->SubAction >= 65 /* SUB_STOP_GATHERING */ ) {
							//worker returning with resource
							continue;
						}

						// unit can't harvest : next one
						if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) {
							unit = NoUnitP;
							continue;
						}

						// Remove from src_c
						units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]];

						// j need one more
						priority_needed[j]++;
					}
				}
			}

			//
			// We just moved an unit. Adjust priority & retry
			//
			if (unit) {
				// i got a new unit.
				priority_needed[i]--;

				// Add to the assigned
				units_assigned[c][num_units_assigned[c]++] = unit;

				// Recompute priority now
				break;
			}
		}
	} while (unit);

	// Unassigned units there can't be assigned ( ie : they can't move to ressource )
	// IDEA : use transporter here.
}
Beispiel #3
0
/**
**  Assign workers to collect resources.
**
**  If we have a shortage of a resource, let many workers collecting this.
**  If no shortage, split workers to all resources.
*/
static void AiCollectResources()
{
	std::vector<CUnit *> units_assigned[MaxCosts]; // Worker assigned to resource
	std::vector<CUnit *> units_unassigned[MaxCosts]; // Unassigned workers
	int num_units_with_resource[MaxCosts];
	int num_units_assigned[MaxCosts];
	int num_units_unassigned[MaxCosts];
	int percent[MaxCosts];
	int priority_resource[MaxCosts];
	int priority_needed[MaxCosts];
	int wanted[MaxCosts];
	int total_harvester = 0;

	memset(num_units_with_resource, 0, sizeof(num_units_with_resource));
	memset(num_units_unassigned, 0, sizeof(num_units_unassigned));
	memset(num_units_assigned, 0, sizeof(num_units_assigned));

	// Collect statistics about the current assignment
	const int n = AiPlayer->Player->GetUnitCount();
	for (int i = 0; i < n; ++i) {
		CUnit &unit = AiPlayer->Player->GetUnit(i);
		//Wyrmgus start
//		if (!unit.Type->BoolFlag[HARVESTER_INDEX].value) {
		if (!unit.Type->BoolFlag[HARVESTER_INDEX].value || !unit.Active) {
		//Wyrmgus end
			continue;
		}

		// See if it's assigned already
		if (unit.Orders.size() == 1 &&
			unit.CurrentAction() == UnitActionResource) {
			const COrder_Resource &order = *static_cast<COrder_Resource *>(unit.CurrentOrder());
			const int c = order.GetCurrentResource();
			units_assigned[c].push_back(&unit);
			num_units_assigned[c]++;
			total_harvester++;
			continue;
		}

		// Ignore busy units. ( building, fighting, ... )
		if (!unit.IsIdle()) {
			continue;
		}

		// Send workers with resources back home.
		if (unit.ResourcesHeld) {
			const int c = unit.CurrentResource;

			num_units_with_resource[c]++;
			CommandReturnGoods(unit, 0, FlushCommands);
			total_harvester++;
			continue;
		}

		// Look what the unit can do
		for (int c = 1; c < MaxCosts; ++c) {
			if (unit.Type->ResInfo[c]) {
				units_unassigned[c].push_back(&unit);
				num_units_unassigned[c]++;
			}
		}
		++total_harvester;
	}

	if (!total_harvester) {
		return;
	}

	memset(wanted, 0, sizeof(wanted));

	int percent_total = 100;
	for (int c = 1; c < MaxCosts; ++c) {
		percent[c] = AiPlayer->Collect[c];
		if ((AiPlayer->NeededMask & (1 << c))) { // Double percent if needed
			percent_total += percent[c];
			percent[c] <<= 1;
		}
	}

	// Turn percent values into harvester numbers.
	for (int c = 1; c < MaxCosts; ++c) {
		if (percent[c]) {
			// Wanted needs to be representative.
			if (total_harvester < 5) {
				wanted[c] = 1 + (percent[c] * 5) / percent_total;
			} else {
				wanted[c] = 1 + (percent[c] * total_harvester) / percent_total;
			}
		}
	}

	// Initialise priority & mapping
	for (int c = 0; c < MaxCosts; ++c) {
		priority_resource[c] = c;
		priority_needed[c] = wanted[c] - num_units_assigned[c] - num_units_with_resource[c];

		if (c && num_units_assigned[c] > 1) {
			//first should go workers with lower ResourcesHeld value
			std::sort(units_assigned[c].begin(), units_assigned[c].end(), CmpWorkers);
		}
	}
	CUnit *unit;
	do {
		// sort resources by priority
		for (int i = 0; i < MaxCosts; ++i) {
			for (int j = i + 1; j < MaxCosts; ++j) {
				if (priority_needed[j] > priority_needed[i]) {
					std::swap(priority_needed[i], priority_needed[j]);
					std::swap(priority_resource[i], priority_resource[j]);
				}
			}
		}
		unit = NULL;

		// Try to complete each resource in the priority order
		for (int i = 0; i < MaxCosts; ++i) {
			int c = priority_resource[i];

			// If there is a free worker for c, take it.
			if (num_units_unassigned[c]) {
				// Take the unit.
				while (0 < num_units_unassigned[c] && !AiAssignHarvester(*units_unassigned[c][0], c)) {
					// can't assign to c => remove from units_unassigned !
					units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]];
					units_unassigned[c].pop_back();
				}

				// unit is assigned
				if (0 < num_units_unassigned[c]) {
					unit = units_unassigned[c][0];
					units_unassigned[c][0] = units_unassigned[c][--num_units_unassigned[c]];
					units_unassigned[c].pop_back();

					// remove it from other ressources
					for (int j = 0; j < MaxCosts; ++j) {
						if (j == c || !unit->Type->ResInfo[j]) {
							continue;
						}
						for (int k = 0; k < num_units_unassigned[j]; ++k) {
							if (units_unassigned[j][k] == unit) {
								units_unassigned[j][k] = units_unassigned[j][--num_units_unassigned[j]];
								units_unassigned[j].pop_back();
								break;
							}
						}
					}
				}
			}

			// Else : Take from already assigned worker with lower priority.
			if (!unit) {
				// Take from lower priority only (i+1).
				for (int j = i + 1; j < MaxCosts && !unit; ++j) {
					// Try to move worker from src_c to c
					const int src_c = priority_resource[j];

					//Wyrmgus start
					//don't reassign workers from one resource to another, that is too expensive performance-wise (this could be re-implemented if the AI is altered to keep track of found resource spots
					break;
					//Wyrmgus end
					
					//Wyrmgus start
					// don't reassign if the src_c resource has no workers, or if the new resource has 0 "wanted"
					if (num_units_assigned[src_c] == 0 || !wanted[c]) {
						continue;
					}
					//Wyrmgus end

					// Don't complete with lower priority ones...
					//Wyrmgus start
					/*
					if (wanted[src_c] > wanted[c]
						|| (wanted[src_c] == wanted[c]
							&& num_units_assigned[src_c] <= num_units_assigned[c] + 1)) {
					*/
					if (wanted[src_c] && ((num_units_assigned[src_c] + 1) * 100 / wanted[src_c]) <= (num_units_assigned[c] * 100 / wanted[c])) { // what matters is the percent of "wanted" fulfilled, not the absolute quantity of needed workers for that resource; add one worker to num_units_assigned[src_c] so that you won't get one worker being shuffled back and forth
					//Wyrmgus end
						//Wyrmgus start
//						continue;
						if (num_units_assigned[c] != 0) { // if there are no workers for that resource, allow reshuffling regardless of proportion calculation, so that if the game starts with few workers (like two), the AI isn't stuck gathering only the first resource it finds
							continue;
						}
						//Wyrmgus end
					}
					
					//Wyrmgus start
					if (num_units_assigned[src_c] <= wanted[src_c]) { // don't reassign if the src_c resource already has less workers than desired
						if (num_units_assigned[c] != 0) {
							continue;
						}
					}
					//Wyrmgus end
					
					//Wyrmgus start
//					for (int k = num_units_assigned[src_c] - 1; k >= 0 && !unit; --k) {
					for (int k = num_units_assigned[src_c] - 1; k >= 0; --k) { // unit may be NULL now, continue instead of breaking loop if so
					//Wyrmgus end
						unit = units_assigned[src_c][k];
						//Wyrmgus start
						if (!unit) {
							continue;
						}
						//Wyrmgus end

						Assert(unit->CurrentAction() == UnitActionResource);
						COrder_Resource &order = *static_cast<COrder_Resource *>(unit->CurrentOrder());

						if (order.IsGatheringFinished()) {
							//worker returning with resource
							continue;
						}

						//Wyrmgus start
						if (unit->Removed || unit->CurrentAction() == UnitActionBuild) { //if unit is removed or is currently building something, it can't be told to harvest (do this here so that AiAssignHarvester returning false later on is only because of unit type not being able to harvest something)
							unit = NULL;
							continue;
						}
						//Wyrmgus end
						
						// unit can't harvest : next one
						if (!unit->Type->ResInfo[c] || !AiAssignHarvester(*unit, c)) {
							unit = NULL;
							continue;
						}

						// Remove from src_c
						units_assigned[src_c][k] = units_assigned[src_c][--num_units_assigned[src_c]];
						units_assigned[src_c].pop_back();

						// j need one more
						priority_needed[j]++;
						
						//Wyrmgus start
						break; //only reassign one worker per time
						//Wyrmgus end
					}
				}
			}

			// We just moved an unit. Adjust priority & retry
			if (unit) {
				// i got a new unit.
				priority_needed[i]--;
				// Recompute priority now
				break;
			}
		}
	} while (unit);

	// Unassigned units there can't be assigned ( ie : they can't move to ressource )
	// IDEA : use transporter here.
}