Computer code for Poisson distribution simulation predicting soccer match goals

Building a Football Score Prediction Simulator with C#, LINQ and the Poisson Distribution

One of the most interesting aspects of software development is applying mathematics and statistics to solve real-world problems. Recently, I developed a small C# console application to explore football score prediction using the Poisson Distribution through the Math.NET Numerics library.

Although the project is relatively simple, it combines several important software engineering concepts, including object-oriented programming, statistical modeling, collections, LINQ, probability analysis, and algorithm design.

Project Goal

The purpose of the application is to estimate the most likely scorelines for a football match based on the average goals scored and conceded by two teams.

The application asks the user to provide:

  • Team 1 name
  • Team 2 name
  • Average goals scored by Team 1
  • Average goals conceded by Team 1
  • Average goals scored by Team 2
  • Average goals conceded by Team 2

Using these values, the system calculates an expected goals value for each team and generates every possible scoreline from 0 to 6 goals.


The Statistical Foundation: Poisson Distribution

The Poisson Distribution is one of the most widely used statistical models in football analytics.

It estimates the probability of a specific number of events occurring within a fixed interval.

In this context:

  • Event = Goal
  • Interval = Football Match

The distribution is represented by:

P(X=k)=\frac{\lambda^k e^{-\lambda}}{k!}

Where:

  • λ (lambda) represents the expected average number of goals.
  • k represents the desired number of goals.
  • P(X=k) represents the probability of scoring exactly k goals.

Rather than implementing the formula manually, the application leverages Math.NET’s built-in Poisson implementation:

Poisson.PMF(media, quantidade);

This returns the probability of scoring exactly a specified number of goals based on the team’s expected goals average.


Modeling Goals with the XGol Class

The first class created in the project is XGol.

This class represents the probability of a team scoring a specific number of goals.

public class XGol

Each object stores:

  • Team name
  • Goal quantity
  • Expected goals average
  • Calculated probability

For example:

Brazil
2 Goals
Expected Goals: 1.80
Probability: 26.77%

Whenever an instance is created, the probability is automatically calculated:

CalculaProbabilidade();

This design centralizes the statistical logic within the class itself, keeping the code organized and easy to maintain.


Modeling Match Results with the Placar Class

The second major class is Placar, which represents an entire match scoreline.

public class Placar

A scoreline is composed of two XGol objects:

new Placar(
new XGol(...),
new XGol(...)
);

Example:

Brazil = 2 Goals
Argentina = 1 Goal

Result:

2x1

The class is responsible for calculating the probability of that specific scoreline and determining the match outcome.


Calculating Scoreline Probability

Assuming the goal-scoring events of each team are statistically independent, the probability of a scoreline is calculated by multiplying both individual probabilities.

Probalidade =
XGolsTime1.Probabilidade *
XGolsTime2.Probabilidade;

Example:

Brazil scoring 2 goals = 26%
Argentina scoring 1 goal = 35%

Therefore:

0.26 × 0.35 = 0.091

Result:

9.1%

This becomes the probability of the 2-1 scoreline occurring.

This simple multiplication is one of the key concepts behind many football prediction models.


Automatically Determining the Match Outcome

The application also determines whether the result is a home win, away win, or draw.

This is accomplished using a nested ternary operator:

Resultado =
(XGolsTime1.Quantidade ==
XGolsTime2.Quantidade)
?
"Draw"
:
(XGolsTime1.Quantidade >
XGolsTime2.Quantidade)
?
$"{XGolsTime1.Nome} Wins"
:
$"{XGolsTime2.Nome} Wins";

Examples:

1x1 = Draw
2x1 = Team 1 Wins
0x3 = Team 2 Wins

This demonstrates how conditional logic can be implemented concisely in C#.


Calculating Expected Goals

Before probabilities can be calculated, the system generates an expected goals value for each team.

The approach used in this project is intentionally simple:

MediaTime1 =
(MediaGolsFeitosTime1 +
MediaGolsSofridosTime2) / 2;
MediaTime2 =
(MediaGolsFeitosTime2 +
MediaGolsSofridosTime1) / 2;

The logic combines:

  • Team A attacking strength
  • Team B defensive weakness

and vice versa.

Example:

Brazil scores: 2.0 goals per game
Argentina concedes: 1.2 goals per game

Expected goals:

(2.0 + 1.2) / 2
= 1.6 expected goals

While simple, this provides a reasonable starting point for building prediction models.

More sophisticated systems could include:

  • Home/Away performance
  • Recent form
  • Offensive strength ratings
  • Defensive strength ratings
  • League averages
  • Expected Goals (xG)

Generating Every Possible Scoreline

The application uses two nested loops:

for (int i = 0; i <= 6; i++)
{
for (int j = 0; j <= 6; j++)
{
}
}

The first loop represents Team 1 goals.

The second loop represents Team 2 goals.

This generates:

0x0
0x1
0x2
...
6x6

Total combinations:

49 scorelines

Each scoreline is stored inside a:

List<Placar>

This collection becomes the foundation for further analysis and ranking.


Using LINQ to Find the Most Likely Results

Once all scorelines have been generated, LINQ is used to identify the most probable outcomes.

placars
.OrderByDescending(x => x.Probalidade)
.Take(5)
.ToList();

This query:

  1. Sorts all scorelines by probability.
  2. Orders them from highest to lowest.
  3. Selects only the top five results.

This is a great example of how LINQ can dramatically simplify data processing while keeping the code readable and expressive.


Example Output

Given the following averages:

Brazil:
2.0 goals scored
1.0 goal conceded
Argentina:
1.5 goals scored
1.2 goals conceded

The application may produce:

1x1 11.52%
2x1 9.84%
1x0 8.93%
2x0 7.62%
0x1 7.18%

In addition, all 49 generated scorelines are displayed in descending probability order.


C# Concepts Practiced

Despite its simplicity, this project covers many important programming concepts:

✅ Classes and Objects

✅ Constructors

✅ Encapsulation

✅ Methods

✅ Lists and Collections

✅ LINQ

✅ Nested Loops

✅ Conditional Operators

✅ Number Formatting

✅ External NuGet Packages

✅ Probability and Statistics

✅ Object-Oriented Programming


Future Improvements

The natural next step for this project is to transform it into a modern desktop application using WinUI 3.

Potential enhancements include:

  • DataGrid-based result visualization
  • Interactive sorting and filtering
  • Probability heat maps
  • Statistical charts and dashboards
  • Team comparison screens
  • JSON import/export
  • Historical match analysis
  • Attack and defense adjustment factors
  • Complete league simulations
  • Monte Carlo simulations
  • Expected Goals (xG) integration

Final Thoughts

One of the best ways to learn software development is by building projects that combine multiple disciplines. This football prediction simulator demonstrates how mathematics, statistics, and programming can work together to create meaningful analytical tools.

More importantly, projects like this reinforce that learning C# is not only about syntax and frameworks—it is about understanding how to transform data into information and information into decisions.

⚽📊💻

#CSharp #DotNet #SoftwareDevelopment #Programming #FootballAnalytics #PoissonDistribution #MathNet #Statistics #LINQ #ConsoleApplication #DataAnalysis #WinUI #Developer #Coding #SoftwareEngineering #SportsAnalytics

Edvaldo Guimrães Filho Avatar

Published by