Need help improving my Flow Field and Unit Avoidance code so that the units can properly surround a unit instead of getting stuck

My setup is like this. Every update my movement system moves all of the units on the field. They use some Boid logic to avoid eachother, and a flow field that directs them towards each other. This is the result. https://imgur.com/a/CaUpfT7

My general setup is to calculate 2 flow fields, one for each player. From every field on the map (32×32 fields) I calculate the vector that points towards the closest enemy. Now this is a fine approach but I’d like to somehow take into consideration obstacles somehow (without another round of for loops)? Or should I handle that in my movement system avoidance? I was maybe thinking of doing A* once a unit is close enough to the enemy but all of these systems together seem rather hacky and overkill.

There are no obstacles currently, maybe a spell will generate it in the future. For now there are only units on the battleground without terrain or obstacles (besides units fighting eachother)

Here is some code. Firstly how I create the flowfields (kinda costly, will look into a way to optimize it further, don’t really need every single tile, can probably merge 2 or 3 together)

func (g *Grid) Update() {     g.entityPositions[0] = g.entityPositions[0][:0]     g.entityPositions[1] = g.entityPositions[1][:0]     entities := g.world.GetEntityManager().GetEntities()     posComps := g.world.GetObjectPool().Components["PositionComponent"]      for _, ent := range entities {         g.entityPositions[ent.PlayerTag] = append(g.entityPositions[ent.PlayerTag], posComps[ent.Index].(components.PositionComponent).Position)     }      for x := 0; x <= g.MaxWidth/FlowTileSize; x++ {         for y := 0; y <= g.MaxHeight/FlowTileSize; y++ {              curPosVector := engine.Vector{X: float32(x * 32), Y: float32(y * 32)}             // find closest tile to this one for both players             minDist := float32(100000.0)             minIndx := -1             for indx, pos := range g.entityPositions[0] {                 d := engine.GetDistanceIncludingDiagonal(pos, curPosVector)                 if d < minDist {                     minIndx = indx                     minDist = d                 }             }              //  fmt.Printf("CurPos : %v, enemyPos : %v,  direction %v \n", curPosVector, g.entityPositions[0][minIndx], g.entityPositions[0][minIndx].Subtract(curPosVector).Normalize())             g.flowTiles[1][x][y].Direction = g.entityPositions[0][minIndx].Subtract(curPosVector).Normalize()              minDist1 := float32(100000.0)             minIndx1 := -1             for indx, pos := range g.entityPositions[1] {                 d := engine.GetDistanceIncludingDiagonal(pos, curPosVector)                 if d < minDist1 {                     minIndx1 = indx                     minDist1 = d                 }             }              g.flowTiles[0][x][y].Direction = g.entityPositions[1][minIndx1].Subtract(curPosVector).Normalize()         }     } }  

And my movement code. A lot of code but just basic allignnment cohesion/ separation. With added 2 look ahead vectors to steer away from the collision.

        desiredDirection := world.Grid.GetDesiredDirectionAt(positionComp.Position, tag)         direction := movementComp.Direction         maxSpeed = movementComp.MovementSpeed          //Avoidance         nearbyEntities := helper.GetNearbyEntities(100, world, index)          avoidance := engine.Zero()         avoidance = avoidance.Add(alignment(world, nearbyEntities, direction).MultiplyScalar(alignmentCoef))         avoidance = avoidance.Add(cohesion(world, nearbyEntities, direction, positionComp.Position).MultiplyScalar(cohesionCoef))         avoidance = avoidance.Add(separation(world, nearbyEntities, direction, positionComp.Position).MultiplyScalar(separationCoef))           //Checking ahead of us whether or not we'll encounter something         lookAheadVectorLong := direction.Add(desiredDirection).MultiplyScalar(maxSpeed * 2.5)         lookAheadVectorShort := direction.Add(desiredDirection).MultiplyScalar(maxSpeed)         maxAvoidanceForce := float32(1.0)          checkPosShort := positionComp.Position.Add(lookAheadVectorShort)         checkPosLong := positionComp.Position.Add(lookAheadVectorLong)          collidedIndexShort := world.Grid.IsPositionFree(index, checkPosShort, positionComp.BoundingBox)         collidedIndexLong := world.Grid.IsPositionFree(index, checkPosLong, positionComp.BoundingBox)          if collidedIndexShort != -1 {             direction = engine.Zero()             avoidance = checkPosShort.Subtract(world.ObjectPool.Components["PositionComponent"][collidedIndexShort].(components.PositionComponent).Position).Normalize()             avoidance = avoidance.MultiplyScalar(maxAvoidanceForce * 1.5)         } else if collidedIndexLong != -1 {             direction = direction.MultiplyScalar(breakingForce)             avoidance = checkPosShort.Subtract(world.ObjectPool.Components["PositionComponent"][collidedIndexLong].(components.PositionComponent).Position).Normalize()             avoidance = avoidance.MultiplyScalar(maxAvoidanceForce * 1.2)         }          direction = desiredDirection         direction = direction.Add(avoidance).Normalize()          positionComp.Position = positionComp.Position.Add(direction.MultiplyScalar(maxSpeed))          positionComp.Position.X = engine.Constraint(positionComp.Position.X, 0, 799)         positionComp.Position.Y = engine.Constraint(positionComp.Position.Y, 0, 511)          movementComp.Direction = direction.Normalize()          world.ObjectPool.Components["PositionComponent"][index] = positionComp         world.ObjectPool.Components["MovementComponent"][index] = movementComp          fmt.Printf("I %d am at %v\n", index, positionComp.Position)     }  }  func limit(p engine.Vector, lim float32) engine.Vector {     if p.X > lim {         p.X = lim     } else if p.X < -lim {         p.X = -lim     }     if p.Y > lim {         p.Y = lim     } else if p.Y < -lim {         p.Y = -lim     }     return p } func alignment(world *game.World, siblings []int, velocity engine.Vector) engine.Vector {     avg := engine.Vector{X: 0, Y: 0}     total := float32(0.0)      for _, siblingIndex := range siblings {         avg = avg.Add(world.ObjectPool.Components["MovementComponent"][siblingIndex].(components.MovementComponent).Direction)         total++     }     if total > 0 {         avg = avg.DivideScalar(total)         avg = avg.Normalize().MultiplyScalar(maxSpeed)         avg = avg.Subtract(velocity)         avg = limit(avg, maxForce)         return avg     }     return engine.Vector{X: 0.0, Y: 0.0}  }  func cohesion(world *game.World, siblings []int, velocity engine.Vector, position engine.Vector) engine.Vector {     avg := engine.Vector{X: 0, Y: 0}     total := float32(0)      for _, siblingindex := range siblings {          avg = avg.Add(world.ObjectPool.Components["PositionComponent"][siblingIndex].(components.PositionComponent).Position)         total++      }     if total > 0 {         avg = avg.MultiplyScalar(1.0 / total * cohesionCoef)         avg = avg.Subtract(position)         avg = avg.Normalize().MultiplyScalar(maxSpeed)         avg = avg.Subtract(velocity)         avg = limit(avg, maxForce)         return avg     }     return engine.Vector{X: 0.0, Y: 0.0} }  func separation(world *game.World, siblings []int, velocity engine.Vector, position engine.Vector) engine.Vector {     avg := engine.Vector{X: 0, Y: 0}     total := float32(0)      for _, siblingIndex := range siblings {         siblingPos := world.ObjectPool.Components["PositionComponent"][siblingIndex].(components.PositionComponent).Position         d := position.Distance(siblingPos)         if d < desiredSeperation {             diff := position.Subtract(siblingPos)             diff = diff.Normalize()             diff = diff.DivideScalar(d)             avg = avg.Add(diff)             total++         }     }     if total > 0 {         avg.DivideScalar(total)     }      if total > 0 {         avg = avg.MultiplyScalar(1.0 / total * separationCoef)         avg = avg.Normalize().MultiplyScalar(maxSpeed)         avg = avg.Subtract(velocity)         avg = limit(avg, maxForce)     }     return avg } 

What I am trying to achieve is:

Units not mashing into each other and just positioning themselves in a free spot around their target.

What are my problems :

  1. Make the flow field direct them away from collision rather than just towards closest unit.
  2. Make it work with the current system without adding too many nested loops and awful checks.
  3. I am doing the avoidance correctly? I have a desired direction that I get from the flow field (that directs me towards closest enemy), then I add avoidance to it to avoid any other units in the area.

My units move really well up untill the point of collision/ going to a spot next to a target. I am not sure how to implemenent that behaviour yet.

(This is my forth iteration of the movement system. I went from pure boid, to grid based, to A*, to this. So I tried a lot of variations and this going surrounding behaviour has been bugging me every time.)

Custom engine (server) written in Golang and then dispatched to Godot for the visuals. Performance is not my concern, but this is a server, so I am more mindful of that, I could probably brute force it but I’d rather hear some better take on it.

Any suggestion/article/ video is greatly appreciated !!

Edit: Thinking about it, flow field is currently useless. I can basically just have a vector pointing to the closest enemy for each unit… Would be less costly as well. But the problem of them clumping and not knowing how to surround still stands.

Maximum (s,t) flow in a simple graph – BIG O

0

Given a simple graph (at most one edge between u-v), with no loops or parallel edges, I have to prove that max (s,t) flow is at most O(v^2 / d^2).

I understand that this is asking to prove max flow <= C* (V^2/d^2) for some positivie c. I asked my TA (teacher assistant) and he said that we’d need to prove this by contradiction

MY PROOF

Assume for a contradiction, there’s more than one edge between some vertices x and y. The shortest path is distance ‘d’. I’m stuck after this. In other words, I need to show that the minimum cut cannot be > v^2 / d^2

Uses of irreducible control flow

What are some applications where irreducible control flow is required*?

I’m particularly interested in programming language features that will be tricky to efficiently compile if the compile target does not permit irreducible control flow. For example, a programming language offering GOTO will generate irreducible control flow for some programs. What other language constructs are like this? For example, is irreducible control flow required to implement resumable exceptions?

I’m also interested in application-level uses of irreducible control flow.

(*: irreducible control flow graphs (CFGs) can be converted to reducible CFGs at some cost, and also some compiler optimizations can convert reducible CFGs to irreducible CFGs, so what I mean is that the original, unoptimized CFG produced by the source code is irreducible. Less formally, I mean that the "programmer’s intent" naturally includes irreducible control flow)

Intuition behind Max Flow Algorithm

Given :

  1. A flow network G whose edges have capacity of 1
  2. G’s maximum flow |F|
  3. A positive integer K

Delete exactly K edges so that the flow of the network is minimized.

So I was asked to develop an algorithm for this but that’s not the issue.I would like to know if my thoughts are in the right direction because the Max Flow – Min Cut Theorem is new to me.

My thoughts :

  1. If K is greater than or equal to |F| delete ALL of the edges that cross G’s Minimum cut and if K is still greater than zero delete random edges and the new max flow is zero

  2. If K is equal to |F| delete ALL of the edges that cross G’s Minimum cut and the new max flow is zero

  3. If K is less than |F| delete K of the edges that cross G’s Minimum cut and the new max flow is |F|-K

Lastly the way that I understand the Max Flow/Min Cut Theorem is that the Min (Source,Sink) cut works as a "bottleneck" to the maximum flow we can push in the network,right?

more than one min cut in a net flow

I know the answer to the question, but I still can’t understand. I have the max flow and I need to determine whether there is more than one min-cut. I know that I need to run BFS from s in the residual network, and from t in the residual net work (after changing each (u,v) to (v,u)). What I can’t understand: why would I get different results? Every example I am trying shows me the same: Any vertex reachable from s (not include s) is a vertex that is reachable from t. Am I wrong? and if I’m wrong, what is the reason some other case can happen?

Checking if two statements can be reached in one control flow

Assume I have a graph representing the control flow and the call graph of a given program. I also have a first and a second statement. I now want to figure out if it is possible to execute both statements (in order) within the same program execution.

Control Flow Graph: I have a graph with all the statements of the program and edges connecting the statements determining the control flow of the program intra function (i.e., within a function).

Call Graph: I also have edges connecting any function call with the start of the function control flow of the called function.

The literature I found concerning control flow covers only intra function flow analysis and the only correct approach I can come up with is a depth first (or breadth first) search starting from the first statement. This, however, hardly feels correct as it is quite cumbersome and I would expect a better solution.

Why doesn’t OIDC hybrid flow require nonce

The OIDC standard requires the nonce parameter in the authentication request when using the implicit flow:

nonce REQUIRED. String value used to associate a Client session with an ID Token, and to mitigate replay attacks.

However in the hybrid flow the nonce is not required. Yet the id_token is directly returned in the response and also susceptible to injection or replay.

Why is the nonce parameter not required in hybrid flow. What secures hybrid flow from injection or replay of id_token?