Jump to content

Dynamic Bot Count


=VG= XOR
 Share

Recommended Posts

Changing the number of bots relative to player count has long been discussed and is somewhat of  a long standing 'unresolved issue' as it can't be done  using configs or without server restart, and given some recent discussion on discord about map difficulty, i figured i might as well post this up as at the very least an alternate 'POSSIBILITY', which at present it is not.

 

So there's two ways to change bot count, either set ENTITY COUNT in server config or change LOGICAL COUNT by manipulating spawn times such that the mean number of bots SPAWNED at any point in time is lower or higher, Classical interpretation of 'count' signifies entity iterations, Logical interpretation that i use here signifies 'INTERACTIVE entity iterations', additionally (re)spawn time based entity count is the most computationally efficient way of dynamically adjusting such count without fucking up server performance, as more bots generally tend to tax a server's performance more, especially in such an old engine.

 

TLDR; It's a fairly trivial script that separates respawn times of bots & players, dynamically not only on round start but through a round as well, as players JOIN & LEAVE. 

It occurs to me that most people don't download attachments & since one needs to login to download attachments anyway, I'll just embed it below, as it's fairly trivial.

SEE CHEATSHEET IN EMBED BELOW TO SEE HOW SPAWN TIME CHANGES RELATIVE TO PLAYER COUNT, it was automatically generated so it should be a fairly accurate representation

import sys, host, bf2, math, realitycore as rcore, realityadmin as radmin

difficultyMultiplier = 5	#lower is more difficult as bots spawn more frequently whereas higher is less difficult as bots spawn less frequently, see cheatsheet below

def init():
	host.registerHandler('PlayerKilled', onPlDeath)	#wounded
	host.registerHandler('PlayerDeath', onPlDeath)	#deded

def onPlDeath(player, soldier):
	if player.isAIPlayer():
		botSpawnTime(player, soldier, difficultyMultiplier)

def  botSpawnTime(player, soldier, difficultyMultiplier):
	botSpawnTime = math.ceil((bf2.playerManager.getNumberOfPlayersInTeam(1) / bf2.playerManager.getNumberOfPlayersInTeam(2)) * difficultyMultiplier)
	radmin.globalMessage("DEBUG: respawning " + str(player.getName()) + " in " + str(player.getTimeToSpawn()) + " sec        [" + str(botSpawnTime) + 	player.setTimeToSpawn(botSpawnTime)
"]secComputed")
	for p in bf2.playerManager.getPlayers():	#redundancy to reset all bot spawn times to specified timer, ignoring PR set spawntimes...
		if p.isAIPlayer():
			p.setTimeToSpawn(botSpawnTime)
	return
"""
	dynamic difficulty scaling applied for every killed & spawned bot,bot respawn time proportional to number of players & number of bots
	there's 2 ways to change bot count, change actual number of bots spawned OR change spawn times to logically define the number of bots active at any given time
	comparison cheat sheet: spawn times @difficulty N for a given Number of players, assuming 40bots
	
	@difficulty=1	40sec@1players	20sec@2players	14sec@3players	10sec@4players	8sec@5players	7sec@6players	6sec@7players	5sec@8players	5sec@9players	4sec@10players	4sec@11players	4sec@12players	4sec@13players	3sec@14players	3sec@15players	3sec@16players	3sec@17players	3sec@18players	3sec@19players	2sec@20players	2sec@21players	2sec@22players	2sec@23players	2sec@24players	2sec@25players	2sec@26players	2sec@27players	2sec@28players	2sec@29players	2sec@30players	2sec@31players	2sec@32players	2sec@33players	2sec@34players	2sec@35players	2sec@36players	2sec@37players	2sec@38players	2sec@39players	1sec@40players
	@difficulty=2	80sec@1players	40sec@2players	28sec@3players	20sec@4players	16sec@5players	14sec@6players	12sec@7players	10sec@8players	10sec@9players	8sec@10players	8sec@11players	8sec@12players	8sec@13players	6sec@14players	6sec@15players	6sec@16players	6sec@17players	6sec@18players	6sec@19players	4sec@20players	4sec@21players	4sec@22players	4sec@23players	4sec@24players	4sec@25players	4sec@26players	4sec@27players	4sec@28players	4sec@29players	4sec@30players	4sec@31players	4sec@32players	4sec@33players	4sec@34players	4sec@35players	4sec@36players	4sec@37players	4sec@38players	4sec@39players	2sec@40players
	@difficulty=3	120sec@1players	60sec@2players	42sec@3players	30sec@4players	24sec@5players	21sec@6players	18sec@7players	15sec@8players	15sec@9players	12sec@10players	12sec@11players	12sec@12players	12sec@13players	9sec@14players	9sec@15players	9sec@16players	9sec@17players	9sec@18players	9sec@19players	6sec@20players	6sec@21players	6sec@22players	6sec@23players	6sec@24players	6sec@25players	6sec@26players	6sec@27players	6sec@28players	6sec@29players	6sec@30players	6sec@31players	6sec@32players	6sec@33players	6sec@34players	6sec@35players	6sec@36players	6sec@37players	6sec@38players	6sec@39players	3sec@40players
	@difficulty=4	160sec@1players	80sec@2players	56sec@3players	40sec@4players	32sec@5players	28sec@6players	24sec@7players	20sec@8players	20sec@9players	16sec@10players	16sec@11players	16sec@12players	16sec@13players	12sec@14players	12sec@15players	12sec@16players	12sec@17players	12sec@18players	12sec@19players	8sec@20players	8sec@21players	8sec@22players	8sec@23players	8sec@24players	8sec@25players	8sec@26players	8sec@27players	8sec@28players	8sec@29players	8sec@30players	8sec@31players	8sec@32players	8sec@33players	8sec@34players	8sec@35players	8sec@36players	8sec@37players	8sec@38players	8sec@39players	4sec@40players



	--default-- default PR bot respawn time is ~60seconds if deded and 0sec if wounded before deded
	@difficulty=5	200sec@1players	100sec@2players	70sec@3players	50sec@4players	40sec@5players	35sec@6players	30sec@7players	25sec@8players	25sec@9players	20sec@10players	20sec@11players	20sec@12players	20sec@13players	15sec@14players	15sec@15players	15sec@16players	15sec@17players	15sec@18players	15sec@19players	10sec@20players	10sec@21players	10sec@22players	10sec@23players	10sec@24players	10sec@25players	10sec@26players	10sec@27players	10sec@28players	10sec@29players	10sec@30players	10sec@31players	10sec@32players	10sec@33players	10sec@34players	10sec@35players	10sec@36players	10sec@37players	10sec@38players	10sec@39players	5sec@40players



	@difficulty=6	240sec@1players	120sec@2players	84sec@3players	60sec@4players	48sec@5players	42sec@6players	36sec@7players	30sec@8players	30sec@9players	24sec@10players	24sec@11players	24sec@12players	24sec@13players	18sec@14players	18sec@15players	18sec@16players	18sec@17players	18sec@18players	18sec@19players	12sec@20players	12sec@21players	12sec@22players	12sec@23players	12sec@24players	12sec@25players	12sec@26players	12sec@27players	12sec@28players	12sec@29players	12sec@30players	12sec@31players	12sec@32players	12sec@33players	12sec@34players	12sec@35players	12sec@36players	12sec@37players	12sec@38players	12sec@39players	6sec@40players
	@difficulty=7	280sec@1players	140sec@2players	98sec@3players	70sec@4players	56sec@5players	49sec@6players	42sec@7players	35sec@8players	35sec@9players	28sec@10players	28sec@11players	28sec@12players	28sec@13players	21sec@14players	21sec@15players	21sec@16players	21sec@17players	21sec@18players	21sec@19players	14sec@20players	14sec@21players	14sec@22players	14sec@23players	14sec@24players	14sec@25players	14sec@26players	14sec@27players	14sec@28players	14sec@29players	14sec@30players	14sec@31players	14sec@32players	14sec@33players	14sec@34players	14sec@35players	14sec@36players	14sec@37players	14sec@38players	14sec@39players	7sec@40players
	@difficulty=8	320sec@1players	160sec@2players	112sec@3players	80sec@4players	64sec@5players	56sec@6players	48sec@7players	40sec@8players	40sec@9players	32sec@10players	32sec@11players	32sec@12players	32sec@13players	24sec@14players	24sec@15players	24sec@16players	24sec@17players	24sec@18players	24sec@19players	16sec@20players	16sec@21players	16sec@22players	16sec@23players	16sec@24players	16sec@25players	16sec@26players	16sec@27players	16sec@28players	16sec@29players	16sec@30players	16sec@31players	16sec@32players	16sec@33players	16sec@34players	16sec@35players	16sec@36players	16sec@37players	16sec@38players	16sec@39players	8sec@40players
	@difficulty=9	360sec@1players	180sec@2players	126sec@3players	90sec@4players	72sec@5players	63sec@6players	54sec@7players	45sec@8players	45sec@9players	36sec@10players	36sec@11players	36sec@12players	36sec@13players	27sec@14players	27sec@15players	27sec@16players	27sec@17players	27sec@18players	27sec@19players	18sec@20players	18sec@21players	18sec@22players	18sec@23players	18sec@24players	18sec@25players	18sec@26players	18sec@27players	18sec@28players	18sec@29players	18sec@30players	18sec@31players	18sec@32players	18sec@33players	18sec@34players	18sec@35players	18sec@36players	18sec@37players	18sec@38players	18sec@39players	9sec@40players
	@difficulty=10	400sec@1players	200sec@2players	140sec@3players	100sec@4players	80sec@5players	70sec@6players	60sec@7players	50sec@8players	50sec@9players	40sec@10players	40sec@11players	40sec@12players	40sec@13players	30sec@14players	30sec@15players	30sec@16players	30sec@17players	30sec@18players	30sec@19players	20sec@20players	20sec@21players	20sec@22players	20sec@23players	20sec@24players	20sec@25players	20sec@26players	20sec@27players	20sec@28players	20sec@29players	20sec@30players	20sec@31players	20sec@32players	20sec@33players	20sec@34players	20sec@35players	20sec@36players	20sec@37players	20sec@38players	20sec@39players	10sec@40players
"""

 

botCountBySpawnTime.py

  • VG Spirit 1
  • Upvote 5
Link to comment
Share on other sites

That's some impressive scritping there, X0R!  Can I assume you've tested this already? Or is that something that would require a dedicated server and a live environment?

@=VG= Melon Muncher @=VG= Fastjack Thoughts? Notes? Feedback?  Can we implement this, or would there be compromises to doing so that would make it undesirable?

If this gets to the live testing phase here, it could be done on our Event Server since it remains offline most of the time, so long as we can set up a proper test environment (maybe dumbed down the key numbers to call a full server "10 people" and low pop "2 people", then scale it up to proper numbers after testing?

This is all assuming that this system does not require a server restart, and that the next map loaded bot count is based on currently connected players.

Yes, we are all about running a stable ship, and it was a year or more before we implemented the VG Maplist Randomizer for 'reasons', but all it takes is time to evaluate all reasons and outcomes, and then take a chance (or not).  We can always roll back or undo any changes if they don't pan out over time.

Happy to have an open discussion about this finally, when 'dynamic bot count' proposals get put on the back burner, it is so they can be brought to the front again before too long, and this is not the first time this has come up.  It deserves proper attention and deliberation for implementation, or good reasons not to.

 :hi: 

  • Thanks 1
  • Upvote 1
Link to comment
Share on other sites

3 hours ago, =VG= SemlerPDX said:

@=VG= Melon Muncher @=VG= Fastjack Thoughts? Notes? Feedback?  Can we implement this, or would there be compromises to doing so that would make it undesirable?

That's outstanding work from him, Semler.

I dont know if we need this for our server(s) because some maps can be handled with 10 players. Doesn`t matter how many bots are on the other side. 

Than we have maps where 40 players aren't enough. The main problems i see are the maps with their asset layout. Khami lrg is horrible. Lashkar Infantry is a meatgrinder (i can and will not understand why we have no logitruck on it). 

But the biggest problem is :

Why we have fucking botspawnpoints inside of controlpoint areas? Or why we have active spawnpoints in a flagcap radius that is linked by another flag? To name one, Kashan with his amazing respawntime of the Milan. Those spawnpoint placements only provoking things like spawnpoint camping or causing missing boots on other important places when the server population is low.

What Coop really need is : DYNAMIC GAMEPLAY

A pythonscript that choose randomly some objectspawners and let spawn their shit. Like the ammocaches in Insurgency.

I want more dynamic gameplay and not the static gameplay we currently have in coop.

I will not have anymore what we have on muttrah like CAS spawning - flying to castle - destroying Milan and AA - 5 minutes later the same again.

 

Here an excample what i want:

I place 50 mortars, 50 bipods and 50 AA-AT static defense in the GPO but the randomcode will only pick 4 mortars, 10 bipods and 2 Static AA's in a random manner. After one of this asset got destroyed another mortar/bipod/static AA-AT will spawn 5-10 minutes later on a different objectspawner, so not on the same position again.

No one at roundstart knows where the mortars/bipod/static AA-AT's will spawn, so CAS has to be carefully, vehicles have to play carefully and the troops also has to Move carefully. No Round will be the same because the shit that will screw up the humans not spawn anymore on the same places.

 

 

  • Like 1
  • Thanks 2
  • Upvote 2
Link to comment
Share on other sites

7 hours ago, =VG= SemlerPDX said:

Can I assume you've tested this already? Or is that something that would require a dedicated server and a live environment?

Tested on both, it was part of a server side minimod i wrote for bf2.

 

7 hours ago, =VG= SemlerPDX said:

Can we implement this, or would there be compromises to doing so that would make it undesirable?

Not that i can think off, that's why i posted it here, to create discourse if nothing else.

 

7 hours ago, =VG= SemlerPDX said:

This is all assuming that this system does not require a server restart, and that the next map loaded bot count is based on currently connected players.

Not only does it not require a server restart, it changes through a round,so if a round starts off with 35people and shrinks to 20people, logical bot count changes accordingly as it's constantly recalculated for every bot killed, there should be no performance penalty.

 

4 hours ago, =VG= Fastjack said:

I dont know if we need this for our server(s) because some maps can be handled with 10 players. Doesn`t matter how many bots are on the other side. 

Absolutely true, however on the flipside, with an appropriate (per testing) difficulty multiplier applied, it makes it more challenging for a more full server, as normally whether or not there's a full server or not, bot respawn time is same as player respawn time, about 60sec by default which  on a full server or even half full, imho is too much.... So one might say it makes maps easier or no easier(depending on difficulty multiplier) for few, inexperienced players and makes maps harder for lots of experienced or inexperienced players alike.

I thought about having it check game tracker to check player's played hours to scale for player experience, but figured that wouldn't go down as well....

4 hours ago, =VG= Fastjack said:

What Coop really need is : DYNAMIC GAMEPLAY

CAN'T AGREE MORE, I'll port over some other scripts for THAT with time, when motivation strikes....
 

 

4 hours ago, =VG= Fastjack said:

Khami lrg is horrible.

Blasphemy, it's one of double's finest, imho. Agree to Disagree.

4 hours ago, =VG= Fastjack said:

A pythonscript that choose randomly some objectspawners and let spawn their shit.

 

4 hours ago, =VG= Fastjack said:

Why we have fucking botspawnpoints inside of controlpoint areas? Or why we have active spawnpoints in a flagcap radius that is linked by another flag? To name one, Kashan with his amazing respawntime of the Milan. Those spawnpoint placements only provoking things like spawnpoint camping or causing missing boots on other important places when the server population is low.

My solution for that was making every bot a contextual spawn point(contingent on vehicle type, cappable cp in range) and making every bot killed a probabilistic contextual respawn trigger(ex. every bot cas kills gets a 60% probability of respawning every shilka on map instead of current fixed respawn time AND 40% probability of cloning spawned shilka on killed bot pos instead of mapper designated pos after a delay), I'll port it at some point, It's part of a much larger script so, it's finicky to clean up.

Started porting but got stuck writing a function to validate position as being in navmesh to prevent bots dying, so i can also use player positions too as that's a more random position seed, and a  2dTo3DPos function to resolve 2d coords to 3d from height map, i'll sort that out too when motivation strikes.

 

4 hours ago, =VG= Fastjack said:

I want more dynamic gameplay and not the static gameplay we currently have in coop.

Me & you both, and just about every person who plays coop.

 

4 hours ago, =VG= Fastjack said:

I place 50 mortars, 50 bipods and 50 AA-AT static defense in the GPO but the randomcode will only pick 4 mortars, 10 bipods and 2 Static AA's in a random manner. After one of this asset got destroyed another mortar/bipod/static AA-AT will spawn 5-10 minutes later on a different objectspawner, so not on the same position again.

 

4 hours ago, =VG= Fastjack said:

No one at roundstart knows where the mortars/bipod/static AA-AT's will spawn, so CAS has to be carefully, vehicles have to play carefully and the troops also has to Move carefully. No Round will be the same because the shit that will screw up the humans not spawn anymore on the same places.

I implemented this at your request, but found a bug that causes python crash after the first dozen selections(which simply changes the minMax (re)spawn time  of random spawners & despawns all on round start), been looking for a fix and what causes that for a while.... still at it, when once again motivation strikes.

  • Like 1
  • VG Spirit 1
  • Upvote 3
Link to comment
Share on other sites

19 hours ago, X0R said:

My solution for that was making every bot a contextual spawn point(contingent on vehicle type, cappable cp in range) and making every bot killed a probabilistic contextual respawn trigger(ex. every bot cas kills gets a 60% probability of respawning every shilka on map instead of current fixed respawn time AND 40% probability of cloning spawned shilka on killed bot pos instead of mapper designated pos after a delay),

Wait, wait, wait .... what?

A spawnlogic that is based on situation on the battlefield and not based on spawn/respawn timers and without fixed positions (objectspawner positions).

Yeah i think that would improve much. Some things can also be handled by mapstrategies but PR's temperature values are lightyears away from that.

I could life with having fixed objectspawners on the map because you can place many of them that make memorizing hard but your idea is great.

 

Would it be possible to spawn a bot with a kit that isn't in the selection menu?

Excample:

Multiple bots got killed by CAS. Now, a trigger let spawn a bot with a manpad.

or :

Multiple bots got killed by tank. Trigger let spawn a bot with a HATkit.

Come on please say YES its possible.

 

  • Like 1
Link to comment
Share on other sites

4 hours ago, =VG= Fastjack said:

Would it be possible to spawn a bot with a kit that isn't in the selection menu?

Excample:

Multiple bots got killed by CAS. Now, a trigger let spawn a bot with a manpad.

or :

Multiple bots got killed by tank. Trigger let spawn a bot with a HATkit.

Come on please say YES its possible.

It's not only possible, it's fairly trivial. The only caveat being, though

host.rcon_invoke("gamelogic.setkit ****")

sets ANY KIT initialized for a given faction in real-time, it changes OVERALL  kit composition of bots on successive re-spawns & not immediately, instead of only INDIVIDUAL bot kits. Which might actually be more ideal, if it's constantly being set to a different kit relative to dominant threat at any given point in time, though not as targeted, it makes it so that if armor dominates the battlefield, a majority of bots that re-spawn do so with AT, though i'm sure you already know of the issue with bots using MANPADS.

There is of course also a targeted solution, a simple (re)spawn handler can be used to shuffle spawns, such that any bot that spawns with AT kit spawns at  pos of any other bot killed by ARMOR(with random delay) for instance, or just at closest spawn point to killer(player).
 

In-fact I recall writing a dumb little function for this, I called it DIE_VERSION, it was supposed to signify that the player was going to die in the next encounter with a bot the player killed.... I digress though, I'll probably pack it along with some other stuff I've written over the years and make a post or something at some point, I've thought about doing so, just got lazy.

  • VG Seal of Approval 1
Link to comment
Share on other sites

15 hours ago, X0R said:

though i'm sure you already know of the issue with bots using MANPADS

Not really, what issue do you mean? Hipfiring is one problem i'm aware about or the old look up wrapper error but that is fixed.

It's also possible to bypass problems - generally done when you think out of the box. 

About the motivation thingy -

i know what you mean and can understand that. In my case i lose focus or interrest because i'm working alone the most time on coop insurgency and there are so many things todo to improve coop but unbearable for one person. To much work.

 

 

  • Thanks 2
Link to comment
Share on other sites

Im liking this whole conversation, some real promise to make the server better here!!

 

2 hours ago, =VG= Fastjack said:

i know what you mean and can understand that. In my case i lose focus or interrest because i'm working alone the most time on coop insurgency and there are so many things todo to improve coop but unbearable for one person. To much work.

X0R, R-DEV when?????

  • Haha 1
Link to comment
Share on other sites

10 hours ago, =VG= Fastjack said:

Not really, what issue do you mean? Hipfiring is one problem i'm aware about or the old look up wrapper error but that is fixed.

Well there you go, never knew that got fixed, was out of the loop for a while, so I guess manpads shouldn't be a problem then.

10 hours ago, =VG= Fastjack said:

It's also possible to bypass problems - generally done when you think out of the box. 

Always.

 

10 hours ago, =VG= Fastjack said:

About the motivation thingy -

i know what you mean and can understand that. In my case i lose focus or interrest because i'm working alone the most time on coop insurgency and there are so many things todo to improve coop but unbearable for one person. To much work.

Touchè brother.

6 hours ago, GRNANDGLD said:

why would you want less bots?

Let's assume you wouldn't, surely you want more bots though, eh? So that's the flip side, depending on how you configure the difficulty multiplier, you can avoid fewer bots while having more for higher player numbers, it's entirely left to configs, like all good things in life 😉

6 hours ago, GRNANDGLD said:

I want random spawn points so nobody Volods the spawns

is that a thing?

It can be, but not on any officially sanctioned server, at least not anytime soon. You'll most likely come across it in events, should anyone choose to use such functionality.  But as for is it possible, absolutely, I've done it & so have others.

  • Like 2
Link to comment
Share on other sites

Just to crystalize a particularly important point I failed to articulate, such a system for the most part  removes server population as a variable in map selection, which meaningfully translates to, ANY MAP CAN BE PLAYED BY ANY NUMBER OF PLAYERS, and thus repetition is reduced and variety of viable maps increased exponentially. Just wanted to put that out there, as I'd forgotten to do so.

  • Upvote 4
Link to comment
Share on other sites

Ya know, some good points where brought up here that made me come to some odd conclusions and I don't even know if this line of thinking is productive, or these things possible...

Say it's low population - the system knows that it shouldn't pelt them with too many bots, so it does what you designed it to do... how about the scenario where no new players join, but this same small team starts killing it - like not just taking points, but steamrolling them.  Can the system have functions to differentiate such swift advancement from what we could term 'standard flow' advancement which typically only sees one to two points capped in a sort period (but not 3, or 4)?

What I'm thinking is that the system could figure out if it's gone 'too' easy on a low pop scenario and adjust accordingly... maybe the 7 people that are on have 4000+ hours each in the game, right?  But it needs to be able to 'be easy' for 6 noobs and a single trained regular player, too, right?

Just thoughts - not trying to make extra work, or anything like that... merely posing the idea or question of possibility (dynamics within dynamics).

 :hi: 

The more I read about this, the more I'm feeling we should make it happen - even if it means some minor requests if feasible, or fine tuning based on feedback from our PR Server Managers here (if you don't mind, of course).  I think the future of PR COOP might very well need this, since the population swing is more of an issue the more PR ages.

  • Thanks 1
Link to comment
Share on other sites

How about a 'retreat' bleed?  BLUFOR has capped all zones, and admins would otherwise need to 'run next' or kill bots streaming out of main for 30+ minutes, so instead, the system notices and issues a 'retreat' which causes OPFOR ticket bleed to increase dramatically and swiftly?  When no admins present, this would allow map to progress swiftly to next map without someone seeking out an Admin just so players don't need to 'run out the clock' as it were.

  • Like 1
Link to comment
Share on other sites

5 hours ago, =VG= SemlerPDX said:

How about a 'retreat' bleed?  BLUFOR has capped all zones, and admins would otherwise need to 'run next' or kill bots streaming out of main for 30+ minutes, so instead, the system notices and issues a 'retreat' which causes OPFOR ticket bleed to increase dramatically and swiftly?  When no admins present, this would allow map to progress swiftly to next map without someone seeking out an Admin just so players don't need to 'run out the clock' as it were.

I already wrote a script that on every CP capped checks if all capable CP's are owned by team 2 && if so sets bot team tickets to 0 after 15 sec triggering end of round by team 2 victory. I'll post it if you wish, should certainly make no-ticket bleed a thing of the past, with rare exceptions like cappable CP in dod for instance. *also attached below* @=VG= SemlerPDX

Spoiler

import sys, host, bf2, math, realitycore as rcore, realityadmin as radmin, realitytimer as rtimer
#simple script to detect end of round on maps where there may be no end of round ticket bleed or where eor ticket bleed might be too slow,and force end of round with victory instead of draw

def init():
	host.registerHandler('ControlPointChangedOwner', onCPStatusChange)

def eorEnum(thisCp):
	for cp in rcore.getControlPoints():
		#if cp is capturable, and is capturable by team 2 and is not owned by team 2 then return as there's at least one capturable flag owned by bot team
		if cp.cp_getParam('unableToChangeTeam') != 1 and cp.cp_getParam('allowCaptureByTeam', 2) == 1 and cp.cp_getParam('team') == 1:
			radmin.globalMessage("bot team owns at least 1 capturable flag")
			return

		#if cp is capturable, and is capturable by team 2 and is not owned by team 2 or team 1 then return as there's at least one capturable flag that remains to be captured.
		if cp.cp_getParam('unableToChangeTeam') != 1 and cp.cp_getParam('allowCaptureByTeam', 2) == 1 and cp.cp_getParam('team') != 2:
			radmin.globalMessage("at least one flag remains to be captured.")	#i.e at least one flag is neutral
			return

	#if this line is reached, all cappable cp's are owned by player team, so set bot team tickets to 0 & trigger end of round
	radmin.globalMessage("End Of Round Will Now Be Forcefully Invoked, As There Appears To Be No Ticket Bleed... GG")
	bf2.gameLogic.setTickets(1, 0)	#set team 1/bot team tickets to 0

def onCPStatusChange(cp, top):
	#invoke end of round check N seconds after any flag status changes
	rtimer.fireOnce(eorEnum, 15, cp)

 

 

forceEndOfRound.py

  • VG Seal of Approval 1
  • Upvote 1
Link to comment
Share on other sites

6 hours ago, =VG= SemlerPDX said:

What I'm thinking is that the system could figure out if it's gone 'too' easy on a low pop scenario and adjust accordingly... maybe the 7 people that are on have 4000+ hours each in the game, right?  But it needs to be able to 'be easy' for 6 noobs and a single trained regular player, too, right?

Should be simple enough, it's a simple matter of adding a factorial that uses KD to evaluate player skill relative to time elapsed, higher KD in a given unit of time signifying higher skilled player. An even simple way to balance this would be to increase the difficulty by .1% for every bot killed and reduce it by 1% for every player killed which results in a more emergent & fluid difficulty balance.

 

Here's an updated script, also attached, that uses a sigmoid function to dynamically adjust difficultyMultiplier based on KD ratio of players for every bot killed, such that the more players there are with high KD ratios the lower the difficultyMultiplier, which translates to higher bot count,  with difficultyMultiplier never exceeding config defind value, such that it' can get exponentially more difficult relative to more experienced players but it never gets easier beyond value defined in config.

tldr;  player KD ratios continually modify difficultyMultiplier with an upper limit for every bot killed and player count continually forces recalculation of bot respawn times relative to player count for every bot killed so as to modify the mean number of bots active in a given unit of time, i.e logical bot count. @=VG= SemlerPDX

Spoiler




import sys, host, bf2, math, realitycore as rcore, realityadmin as radmin

difficultyMultiplier = 5	#lower is more difficult as bots spawn more frequently whereas higher is less difficult as bots spawn less frequently, see cheatsheet below
#value set ABOVE signifies minimum base difficultyMultiplier, such that difficulty will not go higher than this value, such that this will be the LOWEST difficulty, as higher is lower difficulty
difficultyMultiplierMax = difficultyMultiplier

def sigmoid(x):
	#applying the sigmoid function
	return 1 / (1 + math.exp(-x))

def sigmoid_derivative(x):
	#computing derivative to the Sigmoid function
	return x * (1 - x)

def normalize(x, max, min):
	#function that takes a number and normalizes it from 0 - 1 between its min and max bounds.
	return (x - min) / (max - min)

def normalizeInverse(x, max, min):
	#function that takes a number and normalizes it from 0 - 10 between its min and max bounds. then inverts return value so high val becomes low val & vice versa.
	x = max - 1 if x >= max  else x
	x = min if x <= min  else x
	nI = max - max*((x - min) / (max - min))
	return min if nI == 0 else nI
	
def init():
	host.registerHandler('PlayerKilled', onPlKilled)	#wounded, also resolves outright kills if killer
	host.registerHandler('PlayerDeath', onPlDeath)	#deded

def onPlKilled(victim, attacker, weapon, assists, obj):
	attackerKills = attacker.score.kills
	attackerDeaths = attacker.score.deaths
	attackerKD = 1 if attackerKills == 0 or attackerDeaths == 0 else attackerKills/attackerDeaths
	
	#dynamically adjust global difficultyMultiplier on every kill, which logically resolves to the dominant multiplier corresponding to the skill of most dominant players
	#use sigmoid normalisation function to normalise KD to a value between 1 & 0, where if 0 value is changed to 0.1
	#the inverse of the sigmoid is then used as the difficultyMultiplier, inverse is used as lower values signify higher difficulty
	thisDifficulty = normalizeInverse(attackerKD, 10, 1)
	difficultyMultiplier = 	difficultyMultiplierMax if thisDifficulty > difficultyMultiplierMax else thisDifficulty
	#above signifies that difficultyMultiplier will only get as easy as manually set difficulty while being incremented to maximum difficulty based on player performance

	radmin.globalMessage("DEBUG: current difficultyMultiplier is: " + str(difficultyMultiplier))
	
	if victim.isAIPlayer() and not attacker.isAIPlayer():	#if player killed bot
		botSpawnTime(victim, victim, difficultyMultiplier)
	elif victim.isAIPlayer() and attacker.isAIPlayer():	#if bot killed bot, respawn instantly
		victim.setTimeToSpawn(0)
	else:
		botSpawnTime(victim, victim, difficultyMultiplier)

def onPlDeath(player, soldier):
	if player.isAIPlayer():
		botSpawnTime(player, soldier, difficultyMultiplier)

def  botSpawnTime(player, soldier, difficultyMultiplier):
	botSpawnTime = math.ceil((bf2.playerManager.getNumberOfPlayersInTeam(1) / bf2.playerManager.getNumberOfPlayersInTeam(2)) * difficultyMultiplier)
	radmin.globalMessage("DEBUG: respawning " + str(player.getName()) + " in " + str(player.getTimeToSpawn()) + " sec        [" + str(botSpawnTime) + "]secComputed")
	player.setTimeToSpawn(botSpawnTime)
	for p in bf2.playerManager.getPlayers():	#redundancy to reset all bot spawn times to specified timer, ignoring PR set spawntimes...
		if p.isAIPlayer():
			p.setTimeToSpawn(botSpawnTime)
	return


"""
	dynamic difficulty scaling applied for every killed & spawned bot,bot respawn time proportional to number of players & number of bots
	there's 2 ways to change bot count, change actual number of bots spawned OR change spawn times to logically define the number of bots active at any given time
	comparison cheat sheet: spawn times @difficulty N for a given Number of players, assuming 40bots
	
	@difficulty=1	40sec@1players	20sec@2players	14sec@3players	10sec@4players	8sec@5players	7sec@6players	6sec@7players	5sec@8players	5sec@9players	4sec@10players	4sec@11players	4sec@12players	4sec@13players	3sec@14players	3sec@15players	3sec@16players	3sec@17players	3sec@18players	3sec@19players	2sec@20players	2sec@21players	2sec@22players	2sec@23players	2sec@24players	2sec@25players	2sec@26players	2sec@27players	2sec@28players	2sec@29players	2sec@30players	2sec@31players	2sec@32players	2sec@33players	2sec@34players	2sec@35players	2sec@36players	2sec@37players	2sec@38players	2sec@39players	1sec@40players
	@difficulty=2	80sec@1players	40sec@2players	28sec@3players	20sec@4players	16sec@5players	14sec@6players	12sec@7players	10sec@8players	10sec@9players	8sec@10players	8sec@11players	8sec@12players	8sec@13players	6sec@14players	6sec@15players	6sec@16players	6sec@17players	6sec@18players	6sec@19players	4sec@20players	4sec@21players	4sec@22players	4sec@23players	4sec@24players	4sec@25players	4sec@26players	4sec@27players	4sec@28players	4sec@29players	4sec@30players	4sec@31players	4sec@32players	4sec@33players	4sec@34players	4sec@35players	4sec@36players	4sec@37players	4sec@38players	4sec@39players	2sec@40players
	@difficulty=3	120sec@1players	60sec@2players	42sec@3players	30sec@4players	24sec@5players	21sec@6players	18sec@7players	15sec@8players	15sec@9players	12sec@10players	12sec@11players	12sec@12players	12sec@13players	9sec@14players	9sec@15players	9sec@16players	9sec@17players	9sec@18players	9sec@19players	6sec@20players	6sec@21players	6sec@22players	6sec@23players	6sec@24players	6sec@25players	6sec@26players	6sec@27players	6sec@28players	6sec@29players	6sec@30players	6sec@31players	6sec@32players	6sec@33players	6sec@34players	6sec@35players	6sec@36players	6sec@37players	6sec@38players	6sec@39players	3sec@40players
	@difficulty=4	160sec@1players	80sec@2players	56sec@3players	40sec@4players	32sec@5players	28sec@6players	24sec@7players	20sec@8players	20sec@9players	16sec@10players	16sec@11players	16sec@12players	16sec@13players	12sec@14players	12sec@15players	12sec@16players	12sec@17players	12sec@18players	12sec@19players	8sec@20players	8sec@21players	8sec@22players	8sec@23players	8sec@24players	8sec@25players	8sec@26players	8sec@27players	8sec@28players	8sec@29players	8sec@30players	8sec@31players	8sec@32players	8sec@33players	8sec@34players	8sec@35players	8sec@36players	8sec@37players	8sec@38players	8sec@39players	4sec@40players



	--default-- default PR bot respawn time is ~60seconds if deded and 0sec if wounded before deded
	@difficulty=5	200sec@1players	100sec@2players	70sec@3players	50sec@4players	40sec@5players	35sec@6players	30sec@7players	25sec@8players	25sec@9players	20sec@10players	20sec@11players	20sec@12players	20sec@13players	15sec@14players	15sec@15players	15sec@16players	15sec@17players	15sec@18players	15sec@19players	10sec@20players	10sec@21players	10sec@22players	10sec@23players	10sec@24players	10sec@25players	10sec@26players	10sec@27players	10sec@28players	10sec@29players	10sec@30players	10sec@31players	10sec@32players	10sec@33players	10sec@34players	10sec@35players	10sec@36players	10sec@37players	10sec@38players	10sec@39players	5sec@40players



	@difficulty=6	240sec@1players	120sec@2players	84sec@3players	60sec@4players	48sec@5players	42sec@6players	36sec@7players	30sec@8players	30sec@9players	24sec@10players	24sec@11players	24sec@12players	24sec@13players	18sec@14players	18sec@15players	18sec@16players	18sec@17players	18sec@18players	18sec@19players	12sec@20players	12sec@21players	12sec@22players	12sec@23players	12sec@24players	12sec@25players	12sec@26players	12sec@27players	12sec@28players	12sec@29players	12sec@30players	12sec@31players	12sec@32players	12sec@33players	12sec@34players	12sec@35players	12sec@36players	12sec@37players	12sec@38players	12sec@39players	6sec@40players
	@difficulty=7	280sec@1players	140sec@2players	98sec@3players	70sec@4players	56sec@5players	49sec@6players	42sec@7players	35sec@8players	35sec@9players	28sec@10players	28sec@11players	28sec@12players	28sec@13players	21sec@14players	21sec@15players	21sec@16players	21sec@17players	21sec@18players	21sec@19players	14sec@20players	14sec@21players	14sec@22players	14sec@23players	14sec@24players	14sec@25players	14sec@26players	14sec@27players	14sec@28players	14sec@29players	14sec@30players	14sec@31players	14sec@32players	14sec@33players	14sec@34players	14sec@35players	14sec@36players	14sec@37players	14sec@38players	14sec@39players	7sec@40players
	@difficulty=8	320sec@1players	160sec@2players	112sec@3players	80sec@4players	64sec@5players	56sec@6players	48sec@7players	40sec@8players	40sec@9players	32sec@10players	32sec@11players	32sec@12players	32sec@13players	24sec@14players	24sec@15players	24sec@16players	24sec@17players	24sec@18players	24sec@19players	16sec@20players	16sec@21players	16sec@22players	16sec@23players	16sec@24players	16sec@25players	16sec@26players	16sec@27players	16sec@28players	16sec@29players	16sec@30players	16sec@31players	16sec@32players	16sec@33players	16sec@34players	16sec@35players	16sec@36players	16sec@37players	16sec@38players	16sec@39players	8sec@40players
	@difficulty=9	360sec@1players	180sec@2players	126sec@3players	90sec@4players	72sec@5players	63sec@6players	54sec@7players	45sec@8players	45sec@9players	36sec@10players	36sec@11players	36sec@12players	36sec@13players	27sec@14players	27sec@15players	27sec@16players	27sec@17players	27sec@18players	27sec@19players	18sec@20players	18sec@21players	18sec@22players	18sec@23players	18sec@24players	18sec@25players	18sec@26players	18sec@27players	18sec@28players	18sec@29players	18sec@30players	18sec@31players	18sec@32players	18sec@33players	18sec@34players	18sec@35players	18sec@36players	18sec@37players	18sec@38players	18sec@39players	9sec@40players
	@difficulty=10	400sec@1players	200sec@2players	140sec@3players	100sec@4players	80sec@5players	70sec@6players	60sec@7players	50sec@8players	50sec@9players	40sec@10players	40sec@11players	40sec@12players	40sec@13players	30sec@14players	30sec@15players	30sec@16players	30sec@17players	30sec@18players	30sec@19players	20sec@20players	20sec@21players	20sec@22players	20sec@23players	20sec@24players	20sec@25players	20sec@26players	20sec@27players	20sec@28players	20sec@29players	20sec@30players	20sec@31players	20sec@32players	20sec@33players	20sec@34players	20sec@35players	20sec@36players	20sec@37players	20sec@38players	20sec@39players	10sec@40players
"""

 

 

And I welcome any & all feedbacks & suggestion, I posted it here so it might be used by anyone who finds it useful, and so I take no issue with customising it to requests.

botCountBySpawnTime.py

  • VG Spirit 1
  • Thanks 1
  • Upvote 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

Terms of Use and Privacy Policy