Skip to content

Instantly share code, notes, and snippets.

@live-by-unix
Last active June 15, 2026 19:07
Show Gist options
  • Select an option

  • Save live-by-unix/0812d4aa973f3a332f39a207ef43217d to your computer and use it in GitHub Desktop.

Select an option

Save live-by-unix/0812d4aa973f3a332f39a207ef43217d to your computer and use it in GitHub Desktop.
This is a quick python file (needing os, sys, copy, datetime, numpy, pandas) that calculates the chances of different countries winning the world cup. You may change data values. Under MIT license. Run with python3 wincup.py
MIT License
Copyright (c) 2026 live-by-unix
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
import os
import sys
import copy
from datetime import datetime
import numpy as np
import pandas as pd
# ==========================================
# 1. ACCURATE INTERNATIONAL FORM DATA METRICS
# ==========================================
# Ratings are scaled where ~1.00 is the expected tournament baseline average.
# High attack + low defense = elite team profile.
BASE_TEAMS = {
# Europe (UEFA)
"France": {"att": 1.65, "def": 0.72}, "England": {"att": 1.60, "def": 0.70},
"Spain": {"att": 1.55, "def": 0.75}, "Germany": {"att": 1.50, "def": 0.82},
"Portugal": {"att": 1.52, "def": 0.78}, "Italy": {"att": 1.25, "def": 0.65},
"Netherlands": {"att": 1.35, "def": 0.80}, "Belgium": {"att": 1.30, "def": 0.88},
"Croatia": {"att": 1.15, "def": 0.78}, "Denmark": {"att": 1.10, "def": 0.82},
"Switzerland": {"att": 1.12, "def": 0.80}, "Austria": {"att": 1.18, "def": 0.85},
"Ukraine": {"att": 1.05, "def": 0.95}, "Turkey": {"att": 1.12, "def": 1.02},
"Poland": {"att": 1.02, "def": 1.05}, "Hungary": {"att": 0.98, "def": 0.95},
# South America (CONMEBOL)
"Argentina": {"att": 1.68, "def": 0.64}, "Brazil": {"att": 1.58, "def": 0.74},
"Uruguay": {"att": 1.42, "def": 0.76}, "Colombia": {"att": 1.38, "def": 0.81},
"Ecuador": {"att": 1.10, "def": 0.75}, "Chile": {"att": 0.95, "def": 0.98},
# North America (CONCACAF)
"USA": {"att": 1.18, "def": 0.88}, "Mexico": {"att": 1.12, "def": 0.94},
"Canada": {"att": 1.14, "def": 1.02}, "Panama": {"att": 0.90, "def": 1.05},
"Costa Rica": {"att": 0.85, "def": 1.12},
# Africa (CAF)
"Morocco": {"att": 1.28, "def": 0.68}, "Senegal": {"att": 1.22, "def": 0.82},
"Nigeria": {"att": 1.26, "def": 1.04}, "Egypt": {"att": 1.08, "def": 0.88},
"Ivory Coast": {"att": 1.15, "def": 0.92}, "Tunisia": {"att": 0.82, "def": 0.90},
"Algeria": {"att": 1.06, "def": 0.94},
# Asia (AFC)
"Japan": {"att": 1.32, "def": 0.80}, "South Korea": {"att": 1.20, "def": 0.96},
"Iran": {"att": 1.05, "def": 0.88}, "Australia": {"att": 1.02, "def": 0.94},
"Saudi Arabia": {"att": 0.88, "def": 1.14}, "Qatar": {"att": 0.86, "def": 1.20},
"Uzbekistan": {"att": 0.82, "def": 1.05},
# Oceania & Intercontinental Play-offs
"New Zealand": {"att": 0.72, "def": 1.25}, "Peru": {"att": 0.92, "def": 1.02},
"Paraguay": {"att": 0.84, "def": 0.88}, "Ghana": {"att": 1.04, "def": 1.12},
"Cameroon": {"att": 0.98, "def": 1.06}, "Jamaica": {"att": 0.92, "def": 1.14}
}
TEAMS = dict(list(BASE_TEAMS.items())[:48])
# Slice all 48 teams cleanly into 12 groups of 4 to prevent formatting mismatches
all_allocated_teams = list(TEAMS.keys())
group_names = [chr(i) for i in range(65, 77)] # Groups A through L
GROUPS = {group_names[i]: all_allocated_teams[i*4:(i+1)*4] for i in range(12)}
# ==========================================
# 2. CALIBRATED MATCH SIMULATION ENGINE
# ==========================================
def simulate_match_poisson(home_team, away_team, current_teams):
"""
Calculates expected goals using normalized international parameters.
Avg expected goals per match settles accurately between 2.4 and 2.7.
"""
# Global adjustment constant representing average international xG per team
global_baseline_xg = 1.22
home_xg = max(0.05, current_teams[home_team]["att"] * current_teams[away_team]["def"] * global_baseline_xg)
away_xg = max(0.05, current_teams[away_team]["att"] * current_teams[home_team]["def"] * global_baseline_xg)
return np.random.poisson(home_xg), np.random.poisson(away_xg)
def simulate_knockout_match(home_team, away_team, current_teams):
"""Handles elimination scenarios with extra time and balanced penalty variables."""
h_g, a_g = simulate_match_poisson(home_team, away_team, current_teams)
if h_g != a_g:
return home_team if h_g > a_g else away_team
# Extra Time (scaled down to 30 minutes of play)
et_h_g = np.random.poisson(current_teams[home_team]["att"] * current_teams[away_team]["def"] * 0.30)
et_a_g = np.random.poisson(current_teams[away_team]["att"] * current_teams[home_team]["def"] * 0.30)
if et_h_g != et_a_g:
return home_team if et_h_g > et_a_g else away_team
# Penalties (50/50 absolute performance breaker)
return home_team if np.random.rand() > 0.5 else away_team
def run_full_tournament(current_teams):
standings_data = {team: [0, 0, 0] for team in current_teams} # Matrix layout: [Points, Goal Diff, Goals For]
# Round-Robin Group Stage Loop
for g_lbl, g_teams in GROUPS.items():
for i in range(len(g_teams)):
for j in range(i + 1, len(g_teams)):
t1, t2 = g_teams[i], g_teams[j]
g1, g2 = simulate_match_poisson(t1, t2, current_teams)
# Goal Difference adjustments
standings_data[t1][1] += (g1 - g2)
standings_data[t2][1] += (g2 - g1)
# Goals scored tracking
standings_data[t1][2] += g1
standings_data[t2][2] += g2
# Point assignment
if g1 > g2:
standings_data[t1][0] += 3
elif g2 > g1:
standings_data[t2][0] += 3
else:
standings_data[t1][0] += 1
standings_data[t2][0] += 1
# Safe Sorting System utilizing array index lookups to avoid crash exceptions
group_ranks = {}
for g_lbl, g_teams in GROUPS.items():
sub_list = [(t, standings_data[t]) for t in g_teams]
sub_list.sort(key=lambda x: (x[1][0], x[1][1], x[1][2]), reverse=True)
group_ranks[g_lbl] = sub_list
# EXPLICIT STRING EXTRACTION ROUTINE (Prevents list-unpacking tuple data type crash)
auto_qualified = [group_ranks[g][0][0] for g in group_ranks] # Group Winners
runners_up = [group_ranks[g][1][0] for g in group_ranks] # Group Runners-Up
third_places = []
for g in group_ranks:
third_places.append((group_ranks[g][2][0], group_ranks[g][2][1]))
third_places.sort(key=lambda x: (x[1][0], x[1][1], x[1][2]), reverse=True)
wildcards = [item[0] for item in third_places[:8]] # Extract top 8 wildcard strings safely
knockout_pool = auto_qualified + runners_up + wildcards
np.random.shuffle(knockout_pool) # Shuffle to simulate random bracket placements
# Progressive Knockout Tree Elimination Structure
current_bracket = knockout_pool
while len(current_bracket) > 1:
next_bracket = []
for i in range(0, len(current_bracket), 2):
winner = simulate_knockout_match(current_bracket[i], current_bracket[i+1], current_teams)
next_bracket.append(winner)
current_bracket = next_bracket
return current_bracket[0]
# ==========================================
# 3. RUNTIME PIPELINE EXECUTION
# ==========================================
if __name__ == "__main__":
print("\n" + "="*60)
print(" 2026 FIFA WORLD CUP HIGH-PRECISION SIMULATOR ")
print("="*60)
try:
user_input = input("Enter the number of tournament runs (e.g., 100000): ").strip()
sim_runs = int(user_input)
if sim_runs <= 0:
raise ValueError
except ValueError:
print("!! Input Error. Defaulting to 20,000 runs for stability.")
sim_runs = 20000
print(f"\nProcessing {sim_runs:,} tournament iterations across the calibrated engine...")
championship_registry = {team: 0 for team in TEAMS}
for _ in range(sim_runs):
champ = run_full_tournament(TEAMS)
championship_registry[champ] += 1
# Process outputs via Pandas DataFrames
df = pd.DataFrame(list(championship_registry.items()), columns=["Country", "Titles"])
df["Win Chance %"] = round((df["Titles"] / sim_runs) * 100, 3)
df = df.sort_values(by="Win Chance %", ascending=False).reset_index(drop=True)
df.index += 1
print("\n🏆 STATISTICALLY CALIBRATED WIN PROBABILITIES (TOP 15) 🏆")
print("-" * 55)
print(f"{'Rank':<6}{'Country':<25}{'Win Chance':<15}")
print("-" * 55)
for idx, row in df.head(15).iterrows():
country_name = row['Country']
# Print with standard alignment; all special character markers have been removed
print(f"{idx:<6} {country_name:<23}{row['Win Chance %']:>10}%")
print("-" * 55)
absolute_favorite = df.iloc[0]['Country']
favorite_odds = df.iloc[0]['Win Chance %']
print(f"\n🔮 PREDICTION: Based on mathematical form tracking, the team most likely to win the 2026 World Cup is {absolute_favorite.upper()} with a {favorite_odds}% success probability.\n")
@live-by-unix

Copy link
Copy Markdown
Author

To install all deps, run

pip install numpy pandas matplotlib # or pip3 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment