Static table of tactical neighbour links between the 11 formation slots, indexed by formation, plus part of MatchSimulation that checks whether two footballers occupy linked slots on the same team. Provides SetSkillFormation per team and a SkillNeighbors test used for skill synergy.
using System;
namespace Socer;
// Fixed tactical neighbours in the starting formation. Moving, sprinting and
// substitutions keep the slot's connections; distant lines never gain synergy.
public static class FormationSkillNeighbors
{
static readonly bool[][] graphs={
Graph(new[]{(0,2),(0,3),(1,2),(2,3),(3,4),(1,5),(2,6),(3,7),(4,8),
(5,6),(6,7),(7,8),(5,9),(6,9),(6,10),(7,9),(7,10),(8,10),(9,10)}),
Graph(new[]{(0,2),(0,3),(1,2),(2,3),(3,4),(1,5),(2,5),(2,6),(3,6),(3,7),(4,7),
(5,6),(6,7),(5,8),(5,9),(6,9),(7,9),(7,10),(8,9),(9,10)}),
Graph(new[]{(0,1),(0,2),(0,3),(1,2),(2,3),(1,4),(1,5),(2,5),(2,6),(2,7),(3,7),(3,8),
(4,5),(5,6),(6,7),(7,8),(4,9),(5,9),(6,9),(6,10),(7,10),(8,10),(9,10)}),
Graph(new[]{(0,2),(0,3),(1,2),(2,3),(3,4),(1,5),(2,5),(3,6),(4,6),(5,6),
(5,7),(5,8),(6,8),(6,9),(7,8),(8,9),(7,10),(8,10),(9,10)})
};
static bool[] Graph((int First,int Second)[] edges)
{
var graph=new bool[121];
foreach(var edge in edges){graph[edge.First*11+edge.Second]=true;graph[edge.Second*11+edge.First]=true;}
return graph;
}
public static bool AreNeighbors(int formation,int first,int second)=>
first>=0 && first<11 && second>=0 && second<11 && graphs[Math.Clamp(formation,0,graphs.Length-1)][first*11+second];
public static bool AreNeighbors(Formation formation,int first,int second)=>AreNeighbors(Array.IndexOf(Formation.All,formation),first,second);
}
public sealed partial class MatchSimulation
{
readonly int[] skillFormations={0,0};
// Verification can isolate mutual effects without removing individual skills.
// Normal matches always keep both sides enabled.
internal readonly bool[] SkillSynergiesEnabled={true,true};
public void SetSkillFormation(int team,Formation formation)
{if(team>=0 && team<skillFormations.Length)skillFormations[team]=Math.Max(0,Array.IndexOf(Formation.All,formation));}
public bool SkillNeighbors(Footballer first,Footballer second)=>
first!=null && second!=null && first.Team==second.Team && first.Team>=0 && first.Team<2 &&
SkillSynergiesEnabled[first.Team] && first.Id!=second.Id && !first.SentOff && !second.SentOff &&
FormationSkillNeighbors.AreNeighbors(skillFormations[first.Team],first.Id%11,second.Id%11);
}