I have been dissatisfied with my GroupGet library for a while now, but didn't really know how to change it until recently when I got some inspiration from this thread. The result is PruneGroup, a function that lets you pick any number of fittest units from a group instead of only one like GroupGet. The time complexity is O(n*k), where n is the number of units in the group and k is the number of units picked, so if you're only picking a single unit out of the group this function will be no less effective than GroupGet was.

PruneGroup:

libraryPruneGroup//*****************************************************************//* PruneGroup//* written by: Anitarf//*//* PruneGroup is a function that removes units from a group based//* on a user-specified fitness function.//*//* function PruneGroup takes group g, Fitness Function, integer maxUnits, real minFitness returns nothing//*//* The fitness function must follow the Fitness function //* interface that is defined at the start of this library. The//* value it returns specifies a unit's fitness, the units with a//* higher value will remain in the group while the units with a//* lower fitness will be removed from it.//* The maxUnits argument specifies at most how many units may//* remain in the group, but the actual number may be lower if the//* group didn't have that many units in it to begin with or if//* too few units had a fitness higher than minFitness, the last//* argument of the PruneGroup function.//* The fitness limit can be disabled with the NO_FITNESS_LIMIT//* constant value defined in the calibration section, this way//* you can prune a group based only on the unit limit.//*****************************************************************// This is the function interface for the fitness functions.publicfunctioninterfaceFitnesstakesunitureturnsrealglobals// If this constant is passed to the PruneGroup function it will ignore the fitness limit.// This is a random value that is unlikely to be ever used, you don't really need to change it.constantrealNO_FITNESS_LIMIT = -112358.13endglobals// END OF CALIBRATION SECTION // ================================================================globalsprivateFitnessfunc=0privateintegermaxcount=0privaterealminfit=0.0privatebooleanignoreminfit=falseprivateunitarrayfittestprivaterealarrayfitnessprivateintegerarraynextprivateintegerlast=0privateintegercount=0privateintegerN=0endglobalsprivatefunctionEnumtakesnothingreturnsnothinglocalunitu=GetEnumUnit()
localrealfit=func.evaluate(u)
localintegerilocalintegerj// Check if we should bother adding the unit to the list.if (ignoreminfitorfit>minfit) and (count<maxcountorfit>fitness[last]) then// Get a new index and store the unit.setN=N+1setcount=count+1setfittest[N]=usetfitness[N]=fit// Add the index to the sorted list.iflast==0orfit<fitness[last] thensetnext[N]=lastsetlast=Nelseseti=lastloopsetj=next[i]
exitwhenj==0orfitness[j]>=fitseti=jendloopsetnext[N]=next[i]
setnext[i]=Nendif// Remove the last unit from the list if needed.ifcount>maxcountthensetlast=next[last]
setcount=count-1endifendifsetu=nullendfunction// ================================================================functionPruneGrouptakesgroupg, FitnessFunction, integermaxUnits, realminFitnessreturnsnothing// Remember the previous values in case this is interrupting another PruneGroup call.localFitnessf=funclocalintegermc=maxcountlocalrealmf=minfitlocalintegerl=lastlocalintegerc=countlocalintegern=N// Take care of faulty inputs.ifmaxUnits<=0thencallGroupClear(g)
returnendif// Populate the sorted list.setcount=0setlast=0setminfit=minFitnesssetmaxcount=maxUnitssetignoreminfit=minfit==NO_FITNESS_LIMITsetfunc=FunctioncallForGroup(g, functionEnum)
// Repopulate the group from the list.callGroupClear(g)
loopexitwhenlast==0callGroupAddUnit(g, fittest[last])
setlast=next[last]
endloop// Cleanup handle references.loopexitwhenN==nsetfittest[N]=nullsetN=N-1endloop// Return the temporary globals to their previous values.setfunc=fsetminfit=mfsetmaxcount=mcsetignoreminfit=minfit==NO_FITNESS_LIMITsetcount=csetlast=lendfunctionendlibrary

FitnessFunc:

libraryFitnessFuncrequiresPruneGroup//*****************************************************************//* FITNESS FUNCTIONS//* written by: Anitarf//* requires: -PruneGroup//*//* This is a set of functions intended to be used as fitness//* functions for PruneGroup calls, so they all take a unit//* parameter and return a real in accordance with the Fitness//* function interface. The functions provided cover most basic//* criteria according to which users might want to sort units.//* The functions are://*//* FitnessFunc_LowLife - favours units with low life//* FitnessFunc_HighLife - favours units with high life//* FitnessFunc_LowMaxLife - favours units with low max life//* FitnessFunc_HighMaxLife - favours units with high max life//* FitnessFunc_LowMana - favours units with low mana//* FitnessFunc_HighMana - favours units with high mana//* FitnessFunc_LowMaxMana - favours units with low max mana//* FitnessFunc_HighMaxMana - favours units with high max mana//* FitnessFunc_LowDistance - favours units closer to a point//* FitnessFunc_HighDistance - favours units further away from a point//*//* The last two functions need to have a point defined before//* they can be used as a fitness function for PruneGroup, to//* define a point use the following function://*//* function SetFitnessPosition takes real x, real y returns nothing//*****************************************************************//! textmacro PruneFitness_UnitState takes name, statepublicfunctionLow$name$takesunitureturnsrealreturn -GetUnitState(u, $state$)
endfunctionpublicfunctionHigh$name$takesunitureturnsrealreturnGetUnitState(u, $state$)
endfunction//! endtextmacro//! runtextmacro PruneFitness_UnitState("Life", "UNIT_STATE_LIFE")//! runtextmacro PruneFitness_UnitState("Mana", "UNIT_STATE_MANA")//! runtextmacro PruneFitness_UnitState("MaxLife", "UNIT_STATE_MAX_LIFE")//! runtextmacro PruneFitness_UnitState("MaxMana", "UNIT_STATE_MAX_MANA")// ================================================================globalsprivaterealX=0.0privaterealY=0.0endglobalsfunctionSetFitnessPositiontakesrealx, realyreturnsnothingsetX=xsetY=yendfunctionpublicfunctionLowDistancetakesunitureturnsreallocalrealx = GetUnitX(u)-Xlocalrealy = GetUnitY(u)-Yreturn -x*x-y*yendfunctionpublicfunctionHighDistancetakesunitureturnsreallocalrealx = GetUnitX(u)-Xlocalrealy = GetUnitY(u)-Yreturnx*x+y*yendfunctionendlibrary

shouldnt High$name$() return a positive correspondence of the measured state.

maybe associate (bunch) SetFitnessPosition(), with LowDistance() and HighDistance(), respectively, whether by their naming or what

i don't think the 'N' (global) gets restored like the other functional variables of Enum(), in PruneGroup().
can you "insert" an Enum() (or groupenums in general) while another Enum() is running? dont they queue up or work simultaneously?

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
what i mean with all that "Enum() groupEnum" banter is does this interrupt older runs? is it possible to interrupt older ForGroup() runs and how?

what i mean with all that "Enum() groupEnum" banter is does this interrupt older runs? is it possible to interrupt older ForGroup() runs and how?

It is possible if a fitness function itself calls PruneGroup. Since this library supports custom fitness functions, there's no way to ensure that that can't happen, so the code needs to take that possibility into account.

Quote:

Originally Posted by akolyt0r

still you should use private/public for your constants.

No need, the NO_FITNESS_LIMIT is a pretty unique constant name, as for NO_UNIT_LIMIT, I'm thinking of removing it since if you have no unit limit, there's no need to use something as complicated as PruneGroup.

I like this a lot more than your old approach. I've looked over it and don't see any glaring problems, so I think I will approve this and graveyard the old one. Is that okay?

Sure, if you didn't I was going to graveyard GroupGet myself.
I just did a minor update to the first post, I removed the NO_UNIT_LIMIT constant since if you don't have a unit limit then you needn't use PruneGroup in the first place, you can just do a simple ForGroup. Other than that, the code remains the same.