• New Horizons on Maelstrom
    Maelstrom New Horizons


    Visit our website www.piratehorizons.com to quickly find download links for the newest versions of our New Horizons mods Beyond New Horizons and Maelstrom New Horizons!

Needs Testing Intelligent AI retreats and surrenders

Tingyun

Corsair
Storm Modder
UPDATED Oct 18, partial changelog

- Very old bug with flagship surrenders causing fleets to go neutral should be (hopefully) fixed

- Levis's idea of a progression of strength for effects on surrender chances implemented in a progression of powers raising to 0.625, then 0.5, then 0.4 as a given catagory of surrender reason reaches the more extreme stages.

- Demasted enemies should now stop running and try to fight you, please let me know if they do not

- tons new retreat checks, and canceling retreat, under approrpiate cirucmstances, including ammo, shifts in power, etc

- Minor tweaks, cleaning up some tracelog entries, linking them to a debug code, altering some threshold conditions, etc

- unified AIShip ship withdrawal code

- Changed the distance cutoff for counting a ship's power to medium range rather than long.

- Increased forts to 3000HP for powerratio estimation to scare off larger coastal raiders


UPDATED Oct 16

Old first post in spoiler, here is the completely rewritten chanelog (since this is now much expanded from original conception). I've also extensivelly commented the file, please see code and comments for full details.

Bugfixes and improvements

A) Completely rewritten FindPowerRatio function:

Bug fixes:

1) Powerratio function now actually works to count ships (before it had many issues that kept it returning 0 values in most circumstances, including problems with obtaining the distance for the formula, problems with skipping the ship counting itself when alone, etc). Extensive restructuring, and inserting some additional checks like if(rel != RELATION_FRIEND) to avoid skipping self calculation if alone.

2) Powerratio function now counts forts (before it tried to count them with a gethp function that always returned 0)


Improvements:

1) Powerratio function now considers cannon size/quality in addition to just number of cannons (before, it counted 4lbs cannons same as 32 lbs) Cannon price provides the proxy, new function written to return it easily by character for use here. Firepower rating determined by cannon qty times price.

2) Powerratio function no longer treats distance as such a powerful influence, and instead fully counts visible ships (though before the distance calculation was not working anyway, part of was retruning 0 values)

3) Powerratio function discounts ships outside long range, before it tried to count them

4) Powerratio function uses a simple, understandable, and generally valid method--remaining firepower multiplied by remaining hp measured over each ship (with a square root because if you have the same Firepower and hp distributed on two ships, then say 5*5+5*5=50, while 10*10=100, you want them actually equal, and to keep things from shifting too dramatically).


B) Expanded surrender options

Bug Fixes:

1) Powerratio function properly inverted in the function before use in surrender chances (before, the AI was told to think it was stronger when it was actually weaker for the purpose of surrender chance influence, and since the function was broken it more or less amounted to the AI always thinking itself weaker)

Improvements:

1) Before, if morale was above a threshold determined by AI captain leadership, no surrender was possible. Conceptually, surrender was something the crew always forced on an unwilling captain, no captain was free to decide it on their own.

Now, if morale is above that threshold, surrender is still possible if you do huge damage to the enemy hull, outmatch it in firepower, and cut off its retreat by tearing up its sails (but higher morale makes it less likely even then). Now, conceptually, surrender is something the Captain can decide on if his situation is hopeless, in addition to the crew forcing it.

So now there are two conceptual routes to forcing surender, rather than just one, and player tactical options increase in a manner that feels more immersive for roleplaying.

NOTE: Significantly expanded from first version, by carving off the surrender calculations into two seperate paths, the first remains the same as before, morale the primary influence, reflecting the potential for the crew to force a surrender on the captain, the second new one looks primarilly at the tactical situation, and reflects the Captain deciding on his own to surrender.


C) Intelligent Retreats

Bug Fixes:

1) Before, the code for a ship with good morale resuming group task if running away happened before the mechanical check for retreating if damaged, meaning it wouldn't work to cause the AI to resume the group task, they would just be set to runaway again. Now, if the group is not fleeing, a high morale enemy may continue to fight even if it passes the HP theshold (but should still flee if the group is fleeing)

2) The below tactical retreat addition fixes the coastal raiders suiciding against forts bug: Drastically reduce chance of Pirate encounter in friendly ports | Page 3 | PiratesAhoy! A fort has a huge powerratio with its arsenal, and pretty much everything will run from it. However, if an enemy is marked "nosurrender" and could otherwise not retreat because of quest scripting, they should still not retreat (as the new retreat works alongside preexisiting retreats from damage, so they will only now retreat if they could retreat before)

Improvements:

1) Tactical retreats that will cause an AI fleet to run if their fleet is significantly outmatched by the enemies as to make fighting hopeless. Uses the rewritten powerratio determination, and happens after the morale check, so even at high morale they may decide to run. Responds to the changing cirucmstances of battle, as ships lose HP or cannons, or new opponents arrive.

2) Before retreats were either mechanical based on lost HP, or set by somewhat less nuanced cutoffs in screwface functions that looked at things like ship tier and hp in isolation. Those are all still currently in place, though the hard limit on HP running was lowered a bit since it is a very blunt instrument and can be arbitrary, with many of the same concerns handled better in the tactical withdrawls, which does respond to the state of the enemy as well (ie, if you damage an enemy to 39% HP when you are alreadfy at 5%, he would be forced to run. So best to lower that a bit since the tactical withdrawals are more fluid in responding to the current situation)

Also added a check on morale here, so that if the captain is still effectivelly in full control, then he won't run if he is winning heavily by strength ratio, or has no sails.

If the captain is not in full control, ie mroale has passed the surrender threshold, then his ship will run exactly as before based on mechanical considerations (even if quite unwise)

3) Made it a bit more gradiated, with the captain starting to lose a bit of control over his ship when temporary morale becomes within 5 of surrender morale (ie, the run check starts not getting cleared back to group task)


FUTURE STEPS
What I may do later, is to get the screwface functions for retreat to also make use of the new powrratio function, as some of them are aiming for the same thing but with less acurrate measurements.But for now the two systems work alongside one another.


File attached. This one is for playtesting, and as such has various traces enabled so that your compile log with record what is happening with the new stuff.



FEEDBACK note: I have set the tactical withdrawl to happen when the enemy is 2x as powerful or more, and still has ok sails. This might be right (remember, they will still have the normal screwface retreats, like merchants who feel outmatched), or it might be too low, and we should instead set it to 2x. Please provide as detailed feedback as you can about when retreats happen, as well as posting a compile log if you have them, if you feel an interesting retreat happened (ie, a smart one, or a bad one).

Also, note that while generally you must heavily damage a ship before there is any chance of surrender at all, if you are much more than 5x as powerful as the enemy fleet, they might surrender with less damage, and if you are 10x as powerful, they might surrender right away (that would take having 10x their HP and 10x their cannon firepower, because power involves square roots). In general, surrender chance needs a number of "reasons to surrender" disctated by the power difference, with squareroots used to enforce the proper number needed. The number of factors needed at various strength discrepencies will be tweaked based on feedback.

(and that because we are dealing with squareroots, 10x the power might be more like 100x the power really. So maybe only happens if you 100 times powerful really. We'll see how it works out)


So please, report any experiences, and especially would appreciate you attaching the compile logs after you've had a ship battle in a play session, as the traces will tell how everything was assessed.


Had another unexpected break today, so made the attached (early-stage prototype) improvement to the Sea AI.

I extensively commented the changes, so the best way to understand would be to take a look at them in the code I inserted below.

But here is a brief summary:


1) Tactical retreats: AI will be likely to run quickly if heavily outmatched. They do a strength calculation of all friendlies and enemies occasionally during combat, and if they decide their enemies are much more powerful than them, and they still have good sails, they run.

Tests show it seems to solve the suicidal coastal raider AI attacking forts--now, if a coastal raider appears in port, he will quickly turn and run unless he has a chance against the fort (ie, is very powerful ship) (though he might still get killed before he can get away if he is very weak).

Drastically reduce chance of Pirate encounter in friendly ports | Page 2 | PiratesAhoy!

Previously the retreat decisions were mostly based on ship role and hard limits on damage, meaning a little pirate ship would often insist on getting blasted quite to bits by a fort before realizing it was a bad idea to stick around. Now, they figure that out after the first volley. ;)

FEEDBACK REQUEST: I have the AI start to retreat when they estimate the enemy group as twice as powerful as their group (but the power calculations are somewhat blunted, so it might be more like three times their power in realistic terms sometimes). That might need to be changed in response to feedback, but seems to work well so far. Let me know if you think they are running too easily or too late against overwhelming force.

Bear in mind: A) they will only run if they also have halfway decent sails under this check, B) they will still run as before if heavily damaged or if their AI for ship type otherwise tells them to run (this is just an additional possibility, all the past retreat stuff is still in), C) they need to be in combat for a few moments before the check runs, so they might take a moment to assess before fleeing


2) Fixed the usage of the function for the AI checking relative strength

Before, someone had accidentally programmed it backwards in this file--the AI thought they were more powerful when they were weaker, and the reverse as well. So I inverted the number to get the correct usage.

Very weird, probably only escaped notice because the FindPowerRatio function was almost never used (which is crazy, considering the potential to allow the AI to make intelligent withdrawal decisions--instead, it was only used to modify the surrender chance, which of course in the prior incorrect reversed state made the AI less likely to surrender when weaker. :pFixed entirely now, and put to full use.


3) High morale no longer an absolute bar to surrender--if masts broken and hull heavily damaged, a captain may himself tactically decide to surrender even if the crew is still in ok discipline. However, some significant damage to the ship is needed before this can happen.

Also, the old morale threshold is now a modifier, with a gradiated level of modifiers, so decent morale ships are still unlikely to surrender unless you've really pounded them to bits.


4) New influences of sails damage and relative strength on surrender chances. Demasting the enemy ship and heavily outmatching it in firepower will cause it to be likely to surrender earlier, while an enemy that has much greater firepower than you will be less likely to surrender.

(the firepower difference part was intended originally, but because the usage of the powerratio was reversed, the old code accidentially had the opposite of the intended effect, as detailed above. Fixed that and made it have more levels to finetune the influence. )

FEEDBACK REQUEST:

If you have the AI surrender when they shouldn't, let me know, together with as much detail on the circumstances as you can provide (their hull, sails, morale, other ships around, and relative strengths, as best as you remember.)

If the AI doesn't surrender when you think it should, let me know as well.

(this should generally make the AI more likely to surrender than before, because before morale was an absolute bar to surrender, and the FindPowerRatio was reversed in the error so the AI became less likely to surrender the weaker it got.).

So will certainly be finetuning in response to feedback and playtesting.


Most of the changes are below (just a couple of other ones elsewhere in the file, all are commented)


Attached version was made from Levis's latest Oct 7th file. Will be overwitten by any future install of AIShip

File goes into PROGRAM/SEA_AI

Any feedback, especially as detailed in the feedback requests above, is greatly appreciated, so it can be further finetuned if needed.



Further details and examples below of how all this works in a post below!
 

Attachments

  • AIShip.c
    241.1 KB · Views: 266
Last edited:
Since the above is long, I'll provide a quick and simple summary here.


Surrender changes:

Before, if morale was above a threshold determined by AI captain leadership, no surrender was possible. Conceptually, surrender was something the crew always forced on an unwilling captain, no captain was free to decide it on their own.

Now, if morale is above that threshold, surrender is still possible if you do huge damage to the enemy hull, outmatch it in firepower, and cut off its retreat by tearing up its sails (but higher morale makes it less likely even then). Now, conceptually, surrender is something the Captain can decide on if his situation is hopeless, in addition to the crew forcing it.

So now there are two conceptual routes to forcing surender, rather than just one, and player tactical options increase in a manner that feels more immersive for roleplaying.



Intelligent retreat:

Before, many AI combat ships would only run mechanically if reaching a certain threshold of damage.

Now, they still do that, but if they look at the battle situation and judge fighting hopeless because their team is overwhelmingly outmatched, and they still have decent sails, then they will run even if they haven't reached an arbitrary damage cutoff. Happily, I saw a few ships run from forts quickly, which is a good early sign, but will need further testing and work.


I've kept the changes very limited to ensure it won't introduce bugs or break anything, so it should be entirely safe. If you try this out, the worst that could happen is someone surrenders to you too easily, or runs when they shouldn't have, in which case please do report the details of the situation here.
 
Last edited:
Updated version attached to top post. Mainly slight changes to some modifiers based on how strength ratios are compared, and a minor tweak to the powerratio function (setting it to 1.0 instead of 0 under a certain circumstance that would never come up in the way I use it currently, but might make it safer if others use it in other ways going forward).
 
Will test this during the next couple of days, with different ship types for myself and some fleet stuff. You should get the results sometime next week if nothing goes wrong.
Have to be careful though, numbers confuse me incredibly quick.
 
@Cassadar I appreciate that, thanks! :)

There will be a new version in a couple of hours once I finish fixing something, the original powerratio function isn't working right correctly.

(no harm to the game, just the new retreat check never comes back true right now, so currently only surrender changes are operational)

It is taking me awhile to figure out how the function works (I didn't write it, it's been in the game for over a decade, but I will hopefully be able to fix it)
 
Last edited:
Well, that took the entire day and completely rewriting the function. As it turned out, it was completely broken in many ways.

I need to sleep now, and I want to take one more look at the function tomorrow before releasing the new version. In the meantime, if anyone wants to comment on the code, or see what it looks like as a gaff schooner pirstes stares down multiple warships and a fort and decides to run under the new function, here is a link to the compile record and the code for the rewritten powerratio function.

Drastically reduce chance of Pirate encounter in friendly ports | Page 3 | PiratesAhoy!
 
Will also test it the next days ;) BTW: Is it in any way possible that you tell the AI that she wants to boarder you instead of sink? Its quite lame at the beginning of a game when you encounter a rag-tag bunch of pirates with your little sloop and then they just sink me, like "well he might have some loot, but we hate this guy in particular". Or maybe, could the surrender system somehow get adapted to the player?
 
Thanks @Pillat ! :)

Sorry for the delay everyone: since the powerratio function had been used in the prior surrender code, I assumed it worked, and had no idea I would have to rewrite it, which made this 10x more complicated. But, on the plus side, in addition to actually working, the new rewritten function should be much better in design, especially in factoring in cannon strength rather than simply numbers, using distance properly, etc.

Will post by the end of today--I want to rescale the numbers for the surrender chances to the way the new powerratio function generates values, and do a bit more tweaking of it. I really appreciate the playtest offers, and I hope it will be worth the delays. :)

@Pillat , on your boarding idea, sure, I can look at that too for a next project. I might generally focus on AI refinements like that for future projects for awhile, so if there are other behaviors you would like to see, please suggest them as well. i will start a thread for your idea, please do comment there on proposed implementation.
 
BTW, the new rewritten function for powerratio is easy to understand, it is essentially:

1) Skip counting all ships farther than long range
2) Remaining HP multiplied by firepower (firepower = remaining cannon qty multiplied by cannon price, so scaling up the firepower for better cannons), summed over enemy and friendly ships to get fleet power totals
3) take the ratio of enemy ship to friendly ship power
 
@Cassadar @Pillat and any others willing to test:


New version now attached to first post, new changelog posted there too, and ready for testing!

Please let me know your experiences, and also, if you get into any nice battles, please post the compile logs. I've got traces set up so we can see exactly how the various ships saw their chances and such. :)

If you want a preview of how the new stuff works, here is a Pirate ship that was beaten down to 6% of HP, was faced with 3 warships and a fort, and had its sails largely destroyed, and finally decided its situation was hopeless and surrendered (despite having a morale that would not have previously allowed any chance of surrender at all).

You can see it counting the enemy powers first, and then it going through the various factors that increase or decrease its captain motivated surrender decision. These factors are handled with squareroots, creating an effect where you need a certain number of them to have any chance of surrender occurring, as well as with hard limits before it starts checking.

You will also see at bottom it is making a tactical retreat. Of course, it had started retreating right away given the enemy power, I'm just posting its final assessment before surrender. You'll also notice it is rating its own power very low (1) --this is the result of damage to the ship and cannons, the power comparisons and change dynamically as ships are damaged.

Hope that gives you a good idea how it works. Basically, it needed to be absolutelly hopeless, and then it gave up, even though morale is still decent. Expect pirates to fight against desperate odds longer, and merchants to give up a bit sooner.

I look forward to any feedback or compile logs from battles from anyone, thank you to all who help out! :)

SHIP had the following price of cannons1200and HP1095and cannon qty16and contributed the following power4
SHIP had the following price of cannons1200and HP1603and cannon qty12and contributed the following power4
SHIP had the following price of cannons800and HP1143and cannon qty16and contributed the following power3
FORT had the following price of cannons5000and was considered 500 hp for powerratio purposes (see code comments for reasons)and cannon qty68and contributed the following power13
Too far, not counting this ship
Too far, not counting this ship
Too far, not counting this ship
Too far, not counting this ship
Too far, not counting this ship
self calc Character had the following price of cannons1200
power of self is 1.so new total is1.
num of near ships found for is 9, and friend power for is 1.enemy power for is 24.
strengthrat for char Treasure Pirate of ship Standard strengthrat is 4.1667e-002
char Treasure Pirate of ship Standard has decided to run after taking damage, captain decision (may be reversed by group task)
char Treasure Pirate of ship Standardhas decent morale and is continuing 1
char Treasure Pirate of ship Standardis making a tactical withdrawl
SURR chance from morale (crew forced surrender) for char Treasure Pirate of ship Standard surrender chance is 0.His surrender morale limit was 13.154While he currently has temp morale 53.156
Captain surrender is possible, checking Sail percentile41.128strengthrat4.1667e-002HP Percentage6.3123
Captain motivated surrender base surrender chance is5.3079e-006his outmatched factor was0.43609his surrender global modifier is 4.e-002
Feeling outmatched at over 3x, captain has increased his surrender chance to 2.3039e-003
Feeling very outmatched at over 5x, captain has increased his surrender chance to 4.7999e-002
Feeling hopelessly outmatched at over 10x, captain has increased his surrender chance to 0.21909
Moderately scared of sinking, captain has increased his surrender chance to 0.46807
Now very scared of sinking, captain has increased his surrender chance to 0.68415
Moderate damage to sails, captain has increased his surrender chance to 0.82714
A pirate, afraid of hanging, decreasing chance to 0.68415
Sir, the 'Standard' has struck her colors!
 
Last edited:
One important clarification for the update: now, the previous low morale based surrender still operates virtually unchanged from the current version of the base 4.1 mod (I removed one modifier that had never operated anyway because powerratio function was broken in its old form).

The new surrender mechanic now operates alongside the old, with independent chances.

The old one represents the crew forcing the captain to surrender from low morale. The new one represents the Captain deciding his situation is hopeless tactically whatever the crew morale.

So this will not prevent any surrenders that happened before, it just opens up an additional tactical possibility of forcing surrender by attacking the ship rather than the crew morale (and an additional way of being a privateer that focuses more on ship combat at range than grapeshot and boarding, at the cost of getting heavily damaged ships, expanding choice of play styles).

Compile log files and general impressions and feedback from ship battles with surrenders, retreats, or ships getting sunk would be very helpful to aid in balancing. :)
 
Last edited:
Welcome to the wonder world of POTC where a completly broken function still seems to generate some results and it take a long time for someone to actually find its broken :p.

some comments:
Code:
float FindPowerRatio(int idx)
{
    int num = FindNearShips(idx);
    ref chr = GetCharacter(idx);
    int sidx = sti(chr.curshipnum); // change to array 05-06-27
    //if(num == 0) return 0.0;
    string tstr = "rel"+RELATION_ENEMY;
    if(!CheckAttribute(NearShips[sidx],tstr+".0")) return 1.0; // TY Changing from 0 to 1. Just in case, the usage of it in the morale code should never trigger anyway unless enemies are around, but better to be safe.
    float epower = FindPowerOfNearShips(idx, RELATION_ENEMY);
    float fpower = FindPowerOfNearShips(idx, RELATION_FRIEND);
    Trace("num of near ships found for " + chr + "is " + num + ", and friend power for " + chr + "is " + fpower + "enemy power for " + chr + "is " + epower);
    if(epower < 0.01) epower = 0.01; //TY changing to 0.01 to match the new scaling.
    if(fpower < 0.01) fpower = 0.01;
    return fpower / epower;  //TY Let's invert it down here, previously was epower/fpower, and needed to be inverted in above function. Easier to understand this way, and fixes the previous bug.
}

I suggest adding a DEBUG define (just look at smuggling.c or leveling.c on how I've added debug code there) so only if this debug is above a certain value you show the trace message.
You might want to add a case where if idx is smaller then 0 to return. Just to prevent errors from happening. It shouldn't happen. You could add a message there to print to the compile log in case it does happen so you know it happens.
You know for sure the line with the CheckAttribute is okay? I haven't been able to look ingame but you are checking the NearShips array for the ship of the character and you check if it has a attribute which is relX.0 where X is the value of RELATION_ENEMY. Is that right? If you do a DumpAttributes for NearShips[sidx] how does that look?
In the trace message I would change chr to chr.id so you get the character ID. You will just get a pointervalue probably because it's a ref.

Code:
float FindPowerOfNearShips(int idx, int rel)
{
    int num = FindNearShips(idx);
    ref chr = GetCharacter(idx);
    ref    rCannon = GetCannonByType(sti(chr.Ship.Cannons.Type));
    int sidx = sti(chr.curshipnum); // change to array 05-06-27
    string tstr = "rel"+rel;
    float power = 0.0;
   
    if(num <= 0 || !CheckAttribute(NearShips[sidx],tstr+".0")) {
        if(rel != RELATION_FRIEND) { // TY This check not being here before might have been responsible for the self not being counted at all if no friends were around.
        Trace("poweer measuring terminating because nothing to count");
        return power; //0.0; TY commenting out the 0.0, the thing is already set to that.
        }
    }

Same question as above, are you looking for the right attribute?
I believe you made a typo in the trace message ;). Also again I would add a debug statement infront of it.

Code:
    string tstr2;
    for(int i = 0; i < sti(NearShips[sidx].(tstr).qty); i++)
    {
        tstr2 = i;
        chr = GetCharacter(sti(NearShips[sidx].(tstr).(tstr2).idx));
        string s1 = GetCannonCurQuantity(&chr);
        if(GetCannonCurQuantity(&chr)<=0) continue;
Why store this value in a string (s1)?
Why call it agian if you just calculated it. as you aren't using it anywhere else I think you can just remove the s1.

Code:
string s2 = stf(NearShips[sidx].(tstr).(tstr2).dist);
        //TY Begin hard distance check
        if(stf(NearShips[sidx].(tstr).(tstr2).dist) > GetVisibilityRange(1)) { //TY long range is maximum consideration
            Trace("Too far, not counting this ship");
            continue;}
        string s3 = GetCurrentShipHP(&chr); //TY Not sure why these are needed, but leaving in place in case
        string s4 = GetCannonPriceForPower(&chr); // TY the reason for adding strings is beyond my limited understanding, but since the original function had these strings, I'm making one for the new stuff as well
Add a debug statement again infront of the trace message :).
Feel free to remove s2, s3 and s4. They don't seem to have any use (anymore) and only take up resources.

Code:
if(chr.ship.type == SHIP_FORT_NAME) 
        { // TY Just a notes, first, I am leaving the - on forts even as I take the absolute value, as a reminder for people to notice this issue witht he way their stats are being retrieved if they try to work with forts later
        power += sqrt(abs((-500 * GetCannonCurQuantity(&chr) * GetCannonPriceForPower(&chr))/1000000)); //TY Forts are really odd, can't get current hp, return negative numbers, but oh well, this works. See below for reasons we are calculating this, it is basically firepower X remaining HP
        trace("FORT had the following price of cannons" + GetCannonPriceForPower(&chr) + "and was considered 500 hp for powerratio purposes (see code comments for reasons)" + "and cannon qty" + GetCannonCurQuantity(&chr) + "and contributed the following power" + sqrt(abs((-500 * GetCannonCurQuantity(&chr) * GetCannonPriceForPower(&chr))/1000000)));   
        } // TY we take the sqrt here because if you have the same Firepower and hp distributed on two ships, then say 5*5+5*5=50, while 10*10=100, so we need to do squareroots for ships. Blunts powerratio a bit, which in testing works out well
        //TY Moved to 1000 hp after testing, still enough to induce running, but not to overwhelm them into surrender quite so easily Forts aren't mobile anyway, should influence less
        else
        {
        power += sqrt(abs((GetCurrentShipHP(&chr) * GetCannonCurQuantity(&chr) * GetCannonPriceForPower(&chr))/1000000)); //TY slight change--to plan mentioned in other comments--let's use cannon price as a proxy for power
        trace("SHIP had the following price of cannons" + GetCannonPriceForPower(&chr) + "and HP" + GetCurrentShipHP(&chr) + "and cannon qty" + GetCannonCurQuantity(&chr) + "and contributed the following power" + sqrt(abs((GetCurrentShipHP(&chr) * GetCannonCurQuantity(&chr) * GetCannonPriceForPower(&chr))/1000000)));   
        }//TY Explanations of formula. Let's do this differently than before. We need caliber consideration, using weight as a proxy. Distance shouldn't be so overwhelming a factor either. Probably best not to include sails, we don't want ships running when their sails are weak really.
Again, I suggest adding debug statements in front of the trace.

Code:
if(rel == RELATION_FRIEND)  //TY This is where the ship calculates itself, It used to not be calculated at all if the ship was alone because of some bad use of return above
    {
        chr = GetCharacter(idx);
        tstr2 = 0;
        if(GetCannonCurQuantity(&chr)<=0) return power; //TY no reason to do the rest if no cannons
        //power += sqrt(GetCurrentShipHP(&chr) * GetCannonCurQuantity(&chr)) / (1.0 + pow2(stf(NearShips[sidx].all.(tstr2).dist)/100.0, 1.5)); // TY See above for reasons why we are doing this calculqation different
        float selfpower = sqrt(abs((GetCurrentShipHP(&chr) * GetCannonCurQuantity(&chr) * GetCannonPriceForPower(&chr))/1000000)); 
        trace("self calc Character had the following price of cannons" + GetCannonPriceForPower(&chr)); //TY Same comments as above for the formulas
        power += selfpower;
        Trace("power of self is " + selfpower + "so new total is" + power);
    }
Same mention about the comments. And why make a new float called selfpower? you can just say
power += ....the whole formula...
right?

Code:
int GetCannonPriceForPower(ref rChar)
{
    int nCannonType = GetCaracterShipCannonsType(rChar);
    if(nCannonType==CANNON_TYPE_NONECANNON) return 0.0;
    ref rCannon = GetCannonByType(nCannonType);
    trace("Calculating cannon cost at" + sti(rCannon.Cost));
    return sti(rCannon.Cost);
}
it's a int function but if it has CANNON_TYPE_NONECANNON you return a float value (0.0 is float).

Code:
float captainsur = outmatched*SURR_GLOBAL_SCL;  //TY we are starting then with defaults, if 0.1 strength ratio, of 1% surrender here, and at 0.3 strength, half of that, and neglible below. But we want "reasons to surrender" to be needed in addition, so we will first make much less likely, then show the factors that can restore it. 
                captainsur = captainsur*captainsur*captainsur;  //TY To get back to those levels, need at least three plus factor from below, but of course that would only work at quite overhwelming stength, even just be a 1 percent chance at 10x strength, so we need more, will need more factors the close the strengths get
there is a pow(value,power) function ;).
I can see why you want to have it to the third power and I can see why you want to take the sqrt of it each time. But this means it will follow a very steap curve at the start and every added condition will add very few to the eventual value. Maybe you want to consider having a more linear curve there and assign specific percentages to the surrender chance.
things like:
Code:
captainsur *= 0.8;
This will only leave 80% of the initial value.
 
Thinking about this a bit more. There is something we call the FantomType. it's set to the captain and it determines if it's a merchant,pirate or navy etc.
Maybe it's fun to also include that in your check. Normally a merchant will surrender in different circumstances as a pirate does etc.

Also when does the screwface surrender trigger? I for one would prefer it if there is only 1 function which determines when a ship surrenders so I wouldn't mind if 1 of the functions is disabled all together and merged with the other one.
Feel free to take some time. I will probably need some more time to fix stuff also so I don't expect to be able to release a new version before november anyways.
 
What Screwface surrender?


2) Before retreats were either mechanical based on lost HP, or set by somewhat less nuanced cutoffs in screwface functions that looked at things like ship tier and hp in isolation. Those are all still currently in place, though the hard limit on HP running was lowered a bit since it is a very blunt instrument and can be arbitrary, with many of the same concerns handled better in the tactical withdrawls, which does respond to the state of the enemy as well (ie, if you damage an enemy to 39% HP when you are alreadfy at 5%, he would be forced to run. So best to lower that a bit since the tactical withdrawals are more fluid in responding to the current situation)

FUTURE STEPS
What I may do later, is to get the screwface functions for retreat to also make use of the new powrratio function, as some of them are aiming for the same thing but with less acurrate measurements.But for now the two systems work alongside one another.

FEEDBACK note: I have set the tactical withdrawl to happen when the enemy is 2x as powerful or more, and still has ok sails. This might be right (remember, they will still have the normal screwface retreats, like merchants who feel outmatched), or it might be too low, and we should instead set it to 2x. Please provide as detailed feedback as you can about when retreats happen, as well as posting a compile log if you have them, if you feel an interesting retreat happened (ie, a smart one, or a bad one).
 
Thanks for all the advice Levis! I'll be making changes accordingly, here are some comments:

Thinking about this a bit more. There is something we call the FantomType. it's set to the captain and it determines if it's a merchant,pirate or navy etc.
Maybe it's fun to also include that in your check. Normally a merchant will surrender in different circumstances as a pirate does etc.

This one is already in:
Code:
//TY Pirates require an extra factor, merchants require one less than normal. Based on same value jugdement as preexisting surrender chances detailed above (where the surrender morale base was modified)
         if(CheckAttribute(rCharacter, "fantomtype") && rCharacter.fantomtype=="trade")
         {
           captainsur = sqrt(captainsur);
           Trace("A merchant, increasing surrender to " + captainsur);
         }
         if(CheckAttribute(rCharacter, "fantomtype") && rCharacter.fantomtype=="pirate")
         {
           captainsur = captainsur*captainsur;
           Trace("A pirate, afraid of hanging, decreasing chance to " + captainsur);
         }

Also when does the screwface surrender trigger? I for one would prefer it if there is only 1 function which determines when a ship surrenders so I wouldn't mind if 1 of the functions is disabled all together and merged with the other one.

Screwface has retreats, not surrenders. Here is an example of screwface retreats for Pirates. You see it is trying to measure power here, but doing so with much less accuracy than the powerratio function. Instead the code just has the computer ask questions like, how does our HP compare? Yes, no, then move on to, Do we have higher tier ships and more of them?

Tactical withdrawals in the moralecheck code are more of a backstop--to get to 0.5 powerratio, you are really losing pretty bad, because powerratios use squareroots (so it is more like the enemy has 4x your power). The screwface retreats are more about, are we slightly more stronger in the given circumstances. Currently, the tactical withdrawals happen in the morale check code for two reasons: 1) to happen alongside the HP loss withdrawals that already exist, since they serve a similar function but better, and 2) to make sure that ships marked "no surrender" don't use them".

So as long as you are still agreed, and @Pieter Boelen also doesn't mind, I will eventually (after it is refined, later ) update the screwface parts below that look at HP, cannon qty, and ship tier and number to instead use the more accurate and unified measurement of powerratios, while preserving the rest of the good logic, and keeping the differences in how Pirates Merchants and Navy all behave.

Code:
// PIRATES REACTIONS
    if(CheckAttribute(chr1,"fantomtype") && chr1.fantomtype == "pirate")
    {
        // THEY DON'T LIKE WAR SHIPS BUT ATTACK THEM IF THEY HAVE SUPERIOR POWER
        if(CheckAttribute(chr2,"fantomtype") && chr2.fantomtype == "war" || chr2.index == GetMainCharacterIndex())
        {
            //LogIt("Power rgroup : " + Group_GetPowerHP_R(rGroup));
            //LogIt("Power rgroup2 : " + Group_GetPowerHP_R(rGroup2));
            if(Group_GetPowerHP_R(rGroup) >= Group_GetPowerHP_R(rGroup2))
            {
                //LogIt("I'm stronger");
                return true;
            }
            else
            {
                //LogIt("number ships rgroup : " + Group_GetCharactersNumR(rGroup));
                //LogIt("number ships rgroup2 : " + Group_GetCharactersNumR(rGroup2));
                if(Group_GetCharactersNumR(rGroup) >= Group_GetCharactersNumR(rGroup2) && GetCharacterShipClass(chr1) <= GetCharacterShipClass(chr2))
                {
                    //LogIt("j'attaque");
                    return true;
                }
                else
                {
                    // They run away
                    chr1.runaway = "1";
                    if(Ship_GetDistance2D(chr1, chr2) < 200 && GetCannonCurQuantity(chr1)>2 && GetCargoGoods(chr1,GOOD_GUNPOWDER)>0)
                    {
                        if(GetCargoGoods(chr1,GOOD_BALLS)>0 || GetCargoGoods(chr1,GOOD_KNIPPELS)>0) // stop running and defend if a bigger enemy is too close
                        {
                            DeleteAttribute(chr1,"runaway");
                        }
                    }
                    return true;
                }
            }
        }
        else
        {
            // TRADE SHIPS : They attack
            if(GetCharacterShipClass(chr2) == 1)
            {
                // Pirates don't like big escorts
                chr1.runaway = "1";
                return true;
            }
            else
            {
                return true;
            }
        }
    }

Welcome to the wonder world of POTC where a completly broken function still seems to generate some results and it take a long time for someone to actually find its broken :p.

Haha, indeed!

It is actually pretty funny, the powerratio function was completely broken so that the enemy always thought himself stronger, but the usage in surrender code was inverted so in fact that didn't trigger the surrender penalty, so the modifier just never worked or did anything to surrender chances.

So, this function was constantly running and consuming computing power, but never actually changing anything at all, because things were broken in two separate places. ;)


I suggest adding a DEBUG define (just look at smuggling.c or leveling.c on how I've added debug code there) so only if this debug is above a certain value you show the trace message.
You might want to add a case where if idx is smaller then 0 to return. Just to prevent errors from happening. It shouldn't happen. You could add a message there to print to the compile log in case it does happen so you know it happens.

Will do on both.

The traces were just written quickly for me in testing, I'll link them to a debug and/or delete many of them, before the thing is released.




You know for sure the line with the CheckAttribute is okay? I haven't been able to look ingame but you are checking the NearShips array for the ship of the character and you check if it has a attribute which is relX.0 where X is the value of RELATION_ENEMY. Is that right? If you do a DumpAttributes for NearShips[sidx] how does that look?

The checkattribute is one of the few lines that survives unchanged from the original code. I have indeed tested and can confirm it works fine, as I've run several battles and the ships all identify their enemies and friends correctly, and count everyone who should be counted.


In the trace message I would change chr to chr.id so you get the character ID. You will just get a pointervalue probably because it's a ref.

Will do.

Same question as above, are you looking for the right attribute?
I believe you made a typo in the trace message ;). Also again I would add a debug statement infront of it.

Yep on the first part, will do on the second before release.

Why store this value in a string (s1)?
Why call it agian if you just calculated it. as you aren't using it anywhere else I think you can just remove the s1.
...
Add a debug statement again infront of the trace message :).
Feel free to remove s2, s3 and s4. They don't seem to have any use (anymore) and only take up resources.
...
Again, I suggest adding debug statements in front of the trace.

Will do.

The strings I only wrote because that was exactly the way the original function had it. I was really, really confused as to why they were being declared and then not used, and the same functions called twice, but didn't want to delete them before getting expert advice. Now that I have your approval, away they go. ;)

Same mention about the comments. And why make a new float called selfpower? you can just say
power += ....the whole formula...
right?

Only for testing purposes.

The original function had a bug where the self wouldn't be counted under many circumstances (actually, the original function usually wouldn't count any ship BUT the self, but sometimes it wouldn't count the self to, returning 0s for everything). I am pretty sure I figured out why all that was happening, but I want to make sure. By keeping the selfpower seperate, it is easy for me to always see.

It also makes it easier to watch how the ship's dynamic decrease in HP starts influencing its powerratio and surrender chances as a battle progresses in the compile log. Basically, it makes reading the logs easier during testing.


it's a int function but if it has CANNON_TYPE_NONECANNON you return a float value (0.0 is float).

Will correct.


there is a pow(value,power) function ;).
Good point, makes the code easier to read. At first I was exploring having some different numbers for the multiples. Changed to:
pow((0.8 - strengthrat), 3);


I can see why you want to have it to the third power and I can see why you want to take the sqrt of it each time. But this means it will follow a very steap curve at the start and every added condition will add very few to the eventual value. Maybe you want to consider having a more linear curve there and assign specific percentages to the surrender chance.
things like:
Code:
captainsur *= 0.8;
This will only leave 80% of the initial value.

Squareroots are indeed used precisely because they "follow a very steap curve at the start and every added condition will add very few to the eventual value"

Basically, the system is that a Captain needs a certain number of "reasons to surrender" before he will consider doing so. The number of reasons needed are first determined by the powerratio discrency, so if he is close to as powerful as you, he'd need many more reasons, if you completely overwhelm him, he would need fewer reasons. Pirates need 1 more reason, Merchants need one less.

Then, the steep curve at the beginning for the first several squareroots is about getting to the minimum number of reasons for him to consider surrender. Reaching the minimum number of reasons basically gets an unlikely surrender, one more reason, it becomes likely to happen. Getting more reasons after that exerts less of an effect, to avoid making surrender certain. Besides, he already has more than enough reasons, if he is being stubborn or taking time to consider his options, more won't convince him all that much faster.

Let's take a look at it in action in a ship that has been brought to 6% of its HP, using the compile log entries:

Captain motivated surrender base surrender chance is5.3079e-006his outmatched factor was0.43609his surrender global modifier is 4.e-002
Feeling outmatched at over 3x, captain has increased his surrender chance to 2.3039e-003
Feeling very outmatched at over 5x, captain has increased his surrender chance to 4.7999e-002

--Here is where the ship reached its minimum number of reasons to have any real chance of surrender. It had been reduced to 6% of HP, meaning its based surrender chance only required two reasons, which is very low, usually much more is required. Still, it is only at 4% chance of surrender, so absent another reason, it might sink first.

Feeling hopelessly outmatched at over 10x, captain has increased his surrender chance to 0.21909
Moderately scared of sinking, captain has increased his surrender chance to 0.46807
Now very scared of sinking, captain has increased his surrender chance to 0.68415
Moderate damage to sails, captain has increased his surrender chance to 0.82714

--The piling on of reasons is making surrender more likely, but not certain. If the ship had not been quite so overwhelmed, these reasons might have been important in pushing the original surrender decision.

A pirate, afraid of hanging, decreasing chance to 0.68415
Sir, the 'Standard' has struck her colors!

--Here is where the Pirate "needs an additional reason" occurs


The advantages over a linear function are three-fold:
First, it better represents the psychological aspect of forcing a captain to surrender, where it is less about multiples on small numbers and more about the psychological effect of how many reasons he has to consider for surrender. All the reasons focus on pscyhological thresholds, understandable to the player, of things that would motivate a captain to surrender.
Second, it enforces a minimum number needed to reach a theshold, while not making surrender certain by slowing down the growth at the end
Third, and most importantly, it offers independent tactical routes to forcing surrender, expanding player options.

So a player faced with a merchant they have a good pwoer advantage over, can likely demast the merchant, and he will very possiblly surrender, seeing his escape as hopeless. Or he can attack the hull until the powerratios shift or the merchant gets scared of sinking. Faced with a pirate ship, he might have to go more extreme on one strategy, or employ multiple. And a heavy frigate coming across a tier 8 ship, might be able to force it to surrender with much fewer reasons, as his very presence and strength ratio provides several reasons and sets the base number needed low (though he may not get to the 10x strength ratio even, as powers are computed by square roots, so 10x power is really more like 100x, and usually requires you knocking out enemy cannons or beating down their hull).
 
Last edited:
The advantages over a linear function are three-fold:
First, it better represents the psychological aspect of forcing a captain to surrender, where it is less about multiples on small numbers and more about the psychological effect of how many reasons he has to consider for surrender. All the reasons focus on pscyhological thresholds, understandable to the player, of things that would motivate a captain to surrender.
Second, it enforces a minimum number needed to reach a theshold, while not making surrender certain by slowing down the growth at the end
Third, and most importantly, it offers independent tactical routes to forcing surrender, expanding player options.

So a player faced with a merchant they have a good pwoer advantage over, can likely demast the merchant, and he will very possiblly surrender, seeing his escape as hopeless. Or he can attack the hull until the powerratios shift or the merchant gets scared of sinking. Faced with a pirate ship, he might have to go more extreme on one strategy, or employ multiple. And a heavy frigate coming across a tier 8 ship, might be able to force it to surrender with much fewer reasons, as his very presence and strength ratio provides several reasons and sets the base number needed low (though he may not get to the 10x strength ratio even, as powers are computed by square roots, so 10x power is really more like 100x, and usually requires you knocking out enemy cannons or beating down their hull).

All sounds fine indeed, sounds like you had some tought put into it.
Might I suggest thinking about this:
change the sqrt to: pow(value,0.5)
this way we could tweak some things. if you want some things have a bit more weight you can change them to pow(value,0.4) while if you think it's a good reason to surrender but maybe not that weighted you can change it to pow(value,0.75) for example. This would allow for a bit more flexibility and tweaking in the system.

About the screwface functions:
If possible please try to remove them all together and try to make sure everything concerning running away (and surrendering) happens in 1 function. Preferbly 1 which is good readable :). This makes it a lot easier to track down bugs if they ever occur or to expand the system if we want to (say for example we want to add the influence of a certain perk or something like that).
 
All sounds fine indeed, sounds like you had some tought put into it.
Might I suggest thinking about this:
change the sqrt to: pow(value,0.5)
this way we could tweak some things. if you want some things have a bit more weight you can change them to pow(value,0.4) while if you think it's a good reason to surrender but maybe not that weighted you can change it to pow(value,0.75) for example. This would allow for a bit more flexibility and tweaking in the system.

Great idea! I will do so, indeed some of the reasons should be weighted heavier or lighter. :)

About the screwface functions:
If possible please try to remove them all together and try to make sure everything concerning running away (and surrendering) happens in 1 function. Preferbly 1 which is good readable :). This makes it a lot easier to track down bugs if they ever occur or to expand the system if we want to (say for example we want to add the influence of a certain perk or something like that).

Will do on the retreats! I do think the new powerratios will handle the screwface retreat decisions better (and I'll preserve the same value judgements in terms of merchants, pirates, and navy decisions to retreat as currently are done in screwface). Might take me until next week to unify everything, but I'll get them all together in one function. :)

Surrendering is currently all in the AI ship morale check function, and runs in two paths, crew based morale surrender (old system), and captain based tactical surrenders (new system). They are unified in a single function, but kept as two seperate chances, to model the two ways a surrender can happen (and the two tactical options for players to force an enemy to surrender).
 
So as long as you are still agreed, and @Pieter Boelen also doesn't mind, I will eventually (after it is refined, later ) update the screwface parts below that look at HP, cannon qty, and ship tier and number to instead use the more accurate and unified measurement of powerratios, while preserving the rest of the good logic, and keeping the differences in how Pirates Merchants and Navy all behave.
I am not paying proper attention here, I'm afraid.
Too much else on my mind to be able to spend a lot of mental capacity on this one as well.
 
That's ok Pieter, to simplify, all I'd be doing is taking things where the AI asks:

"Do I have more cannons than the enemy? Do I have more ships? Do I have more total HP? Is my ship higher tier?"

In a decision tree that asks one question after another and leads to either retreat or fight, and replace them with:

"Is my power (computed by summing firepower and HP over the fleet) greater than ____ times the enemy power?"

I think that should be a fairly uncontroversial and substantial improvement, but I try to be conservative about changing things without approval. :)
 
Back
Top