Build a 'weight'-based-simulation (Yeah, I just now invented that term). Each variable (regardless of its type) has a 'weight'. For example, players have weights. A good player has extra weight. A player with an injury has less weight or even no wait at all (or maybe negative weight?).
You add all the weight together (of both teams, because it is a soccer match). That weight resembles a winning chance percentage. For example;
The weight of Team A = 56, the weight of Team B = 120
The weight already shows that one team is much better (regardless of how the weight was established .. maybe they have very round balls, who cares) than the other.
Based on the weight, you could calculate a winning chance;
The winning chance of Team A = 32%, The winning chance of Team B = 68%.
Now you could write an algorithm that simulates a match, influenced by the winning percentage. I wrote an algorithm like this once to draw advertisements. In my case, the number of clicks an advertisement had was the weight. The bigger the weight, the more chance the advertisement was picked by my algorithm.
I wrote the algorithm by taking a large number (like, 1000) and then assigned a range of that number to each advertisement, based on the weight percentage. In this case, Team A gets a range of 32% of 1000, which is 0 - 320, Team B gets a range of 68% which is 321 - 1000. Then my algorithm would draw a number (randomly) between 0 and 1000. The advertisement (or your teams) with the largest range (and thus largest winning chance) has the most chance of being picked by the algorithm, although it could turn out differently.
This kind of algorithm is great (although not perfect) for a balanced outcome (if users could create their own teams, buy better players, etc). You could also make any events within the game drawn by this algorithm, simply by adding a weight to the event as well..
You could add weight to an event (for example the injury of a team mate), per team, based on other weight factors within that team (how many matches played in a row, how good is (or how much weighs) their medic staff, etc). If you do the weight thing right, you could get a very balanced (and easily expandable) simulation algorithm that can both be predictable (just like some matches in real life) or totally surprising (again, just like a real life match).
UPDATE:
Tactical Influences
You added tactical influences, plus the question 'how would you do it?', so I will elaborate. What you are currently doing (as I understand it) is you take a percentage (the chance something occurs) and multiply that with a ratio, so that it will occur more/less.
However, because you can have multiple ratio's, you end up with a chance more then 100%.
First of all, for every tactical advantage of a team, there is (probably) a counter advantage on the other team. For example, if Team A has a weight in making goals, Team B has a counter weight in stopping goals. This sum is the universe (100%). Now the weight of both tactical advantages makes up a piece of that universe, or total weight (as I explained above).
Say that Team A is 80% certain of scoring a goal, in a certain minute, and Team B is 20% certain of stopping it (based on the weight system). But, because Team B just acquired a very good keeper, there is a tactical influence on Team B's side. This influence should shift the chance of an event, but not the universe itself! In other words, you shouldn't end up with a total chance of more then 100% (although in some cases, this isn't necessarily a bad thing)
So, you should add weight to Team B, based on the tactical influence and then re-calculate the chances based on the new weights.
Assigning Weight
Now, like you commented, assigning weight isn't easy. Certainly not if you have to 'weigh' players on their qualities. Weighing is about more then just saying that a player is 'bad' or 'good', you have to actually grade them (like in high school!). The bigger the highest grade, the more accurate the weighting system is.
Now, assigning weights to tactical influences is a bit more easier. Say that you have the following influences;
- Stopping goals
- Scoring goals
- Defence
- Attack
Now, create a pool of total weight (say, 1000, I like that number). These are 'tactical points' you could assign. These four influences make up a match, so you could assign 250 points to each influence. This number (250) is the universe of each influence.
The assignment of these points, per team, depends on the team's weight factors (like, do they have a good keeper?)
A keeper, for instance, weighs against the opponents keeper (and maybe also the people that are in between the keeper and the opponent, but let's keep it simple). Say the keeper of Team A weighs 80% of the total, and the keeper of Team B 20%. This rates how good they are, which is directly related to the tactical points they get. So Team A gets 80% of 250 stopping-goals-points and Team B gets 20% of those points.
The rest of the points can be assigned equally. In my example, I took only two keepers as the universe of wether a goal gets stopped or not. In reality, there could be a lot more weight factors (for you to figure out).
Once they are all divided, you can use the tactical points to make out the match. For each minute you could re-calculate the chance of winning. Each minute, you could also re-calculate the tactical influences (say another player enters the field, or a player is injured).
Yes, you will get a LOT of variables. But the more you get, the better a match plays. The more variables (or weights / counter weights) the more it feels like real life.
In general, it looks like this is a fairly complicated problem, and I'm not sure how efficient you'll get it.
That said, I have seen some things which would decidedly help you.
First I would type the variables in the parameters. This may not necessarily make your code faster, but it would make it easier to read and debug. Next, I would remove the $teamname_att, $teamname_def parameters and simply have those as values in the associative $strength_att, $strength_def arrays. Since this data is always paired up anyway, this will reduce the risk of accidentally using one team's name as a reference to the other team.
This will make it so you will not have to continually look up values in arrays:
// replace all $tactics[$teamname_att] with $attackers
$attackers = $tactics[$teamname_att];
$defenders = $tactics[$teamname_def];
// Now do the same with arrays like $_POST[ "team1" ];
You have three helper functions which all have the pattern:
function foo( $arg ){
$bar = $arg * $value;
return $bar;
}
Since this means that you have to create an extra variable (something which can be costly) each time you run the function, use these instead:
function tactics_weight($wert) {
return $wert*0.1+0.8;
}
function strengths_weight($wert) {
return log10($wert+1)+0.35;
}
/*
Perhaps I missed it, but I never saw Chance_Percent( $num1, $num2 )
consider using this function instead: (one line instead of four, it also
functions more intuitively, Chance_Percent is your chance out of 100
(or per cent)
function Chance_Percent( $chance ) {
return (mt_rand(1, 100) <= $chance);
}
*/
function Chance_Percent($chance, $universe = 100) {
$chance = abs(intval($chance)); // Will you always have a number as $chance?
// consider using only abs( $chance ) here.
$universe = abs(intval($universe));
return (mt_rand(1, $universe) <= $chance);
}
I couldn't help but notice this pattern coming up consistently:
$matchReport .= ' ' . comment_action($teamname_att, 'attack');
My general experience is that if you move the concatenation of $matchReport into comment_action, then it will be just slightly faster (Generally less than a dozen milliseconds, but since you're calling that function a half-dozen times inside of a recursive function, this could shave a couple tenths of a second per running).
I think that this would flow much better (both from a reader's perspective, and from
Finally, there are several times where you will use the same call to the same function with the same parameter. Make that call up front:
$goalieStrength = strengths_weight($strength_def['goalkeeper']);
Hope this helps.
Best Answer
Here is a way I would do it.
Offensive/Defensive Quality
First lets work out the average strength of the entire team:
Now we want to split out side in two, and work out the average for our defensive players, and our offensive players.]
Now, we have three values. The first, out Team average, is going to be used to calculate our odds of winning. The other two, are going to calculate our odds of defending and our odds of scoring.
A team with a high offensive average is going to have more chances, a team with a high defense is going to have more chance at saving.
Now if we have to teams, lets call them A and B.
Team A, have an average of 80, An offensive score of 85 and a defensive score of 60.
Team B, have an average of 70, An offensive score of 50 and a defensive score of 80.
Now, based on the average. Team A, should have a better chance at winning. But by how much?
Scoring and Saving
Lets work out how many times goals Team A should score:
I have assumed the random value adds anything between -1 and +1, although you can adjust this.
As we can see, the formula indicates team A should score 1.6 goals. we can either round this up/down. Or give team A 1, and calculate if the other one is allowed (random chance).
Now for Team B
So we have A scoring 1 and B scoring 1. But remember, we want to weight this in A's favour, because, overall, they are the better team.
So what is the chance A will win?
So we can see the odds are 14% (.14) in favor of A winning the match. We can use this value to see if there is any change in the final score:
If our random number was 0.8, then the match is a draw.
Rounding Up and Further Thoughts
You will definitely want to play around with the values. Remember, game mechanics are very hard to get right. Talk to your players, ask them why they are dissatisfied. Are there teams always losing? Are the simulations always stagnant? etc.
The above outline is deeply affected by the randomness of the selection. You will want to normalise it so the chances of a team scoring an extra 5 goals is very very rare. But a little randomness is a great way to add some variety to the game.
There are ways to edit this method as well. For example instead of the number of goals, you could use the Goal figure as the number of scoring chances, and then have another function that worked out the number of goals based on other factors (i.e. choose a random striker, and use that players individual stats, and the goalies, to work out if there is a goal)
I hope this helps.