Machine Learning
Predicting CS:GO Round Winners
Building a machine learning model to predict the winning team for CS:GO rounds using match event data.
Machine Learning
CS:GO Round Winner Predictor
Predicting the winner of a Counter-Strike: Global Offensive round using match state, player statistics, and event-level signals.
Background
Counter-Strike has always been one of my favorite competitive games. If you’ve never played it before, each match is made up of a series of short rounds where two teams compete to either plant or defuse a bomb. Every round is a mix of strategy, teamwork, and split-second decisions, and even a small advantage can completely change the outcome.
That got me wondering: could a machine learning model predict who would win a round before it actually ended? This project takes round-level match data, engineers features from the game state, and compares multiple machine learning models to see how accurately they can predict the winning team.
Goal
The goal was to build a machine learning pipeline that takes the current state of a round and predicts which team is most likely to win. Starting with raw match data, the workflow cleans the data, engineers meaningful features, trains multiple classification models, and evaluates which approach performs best.
From raw match events to a trained model, each stage transforms the data into something the algorithm can learn from before making a round winner prediction.
Dataset
The dataset consists of round-by-round match data from competitive CS:GO games. Each row represents a single round and captures the state of the match before the winner is known, including the current score, team economy, and key events that happened during the round.
What’s in the Dataset?
- team_ct_money and team_t_money: the total money available to each team before the round
- team_ct_score and team_t_score: the current match score
- last_round_winner: which team won the previous round
- bomb_planted: whether the attacking team successfully planted the bomb
- round_winner: the target variable the model tries to predict
The prediction target is round_winner, which can be either the Counter-Terrorists (CT) or Terrorists (T). The dataset is fairly balanced, with both teams winning a similar number of rounds, making it well suited for a binary classification problem.
I also engineered additional features, such as economy differences and pistol rounds, to better capture the state of the game before each prediction.
The dataset is well balanced, with both teams winning a similar number of rounds. That means the model has to learn meaningful gameplay patterns rather than simply predicting the majority class.
Preparing the Data
Raw match data isn’t something you can feed directly into a machine learning model. Before training, I cleaned the dataset, encoded categorical variables, and created a few additional features that better describe the current state of a round.
Some of the most useful engineered features were:
- score_diff: the score gap between the two teams.
- economy_diff: the difference in available money before the round.
- last_round_winner: adds a simple measure of momentum from the previous round.
Finally, I standardized the continuous features before training. It keeps everything on a similar scale and generally helps models learn more effectively.
import pandas as pd
from sklearn.preprocessing import LabelEncoder, StandardScaler
raw = pd.read_csv("src/dataset.txt")
raw["score_diff"] = raw["team_ct_score"] - raw["team_t_score"]
raw["economy_diff"] = raw["team_ct_money"] - raw["team_t_money"]
encoder = LabelEncoder()
raw["last_round_winner"] = encoder.fit_transform(raw["last_round_winner"])
raw["round_winner"] = encoder.fit_transform(raw["round_winner"])
scaler = StandardScaler()
raw[["score_diff", "economy_diff"]] = scaler.fit_transform(raw[["score_diff", "economy_diff"]])
The preprocessing pipeline transforms raw match data into a clean feature set that's ready for model training.
Exploring the Data
Before training any models, I wanted to understand what the data was actually telling me. A few quick visualizations helped confirm which features looked promising and whether there were any obvious relationships worth keeping.
Most rounds happen when the score is still fairly close, which means the model can't rely on score alone. It needs to consider several other factors before making a prediction.
Economy is one of the biggest strategic elements in Counter-Strike. Even a small money advantage can mean better weapons and utility, which often translates into a stronger chance of winning the round.
One thing I always like checking is a correlation matrix. It gives a quick overview of which features tend to move together and helps catch redundant information before training a model.
A few relationships stood out immediately. Player counts and time remaining were strongly correlated, while economy and score showed a more moderate relationship. None of the features looked overly dependent on each other, which was a good sign for model training.
Training the Models
I wasn’t interested in training just one model and calling it a day. Instead, I compared three different classification algorithms to see how they handled the same dataset and which one was best suited for predicting round winners.
- K-Nearest Neighbors (KNN) compares the current round to similar rounds from the past and predicts the outcome based on its nearest neighbors.
- Logistic Regression serves as a simple baseline, learning a linear relationship between the game state and the winning team.
- Random Forest combines hundreds of decision trees to capture more complex patterns, like how economy, score, and players alive interact during a round.
models = {
"KNN": KNeighborsClassifier(),
"Random Forest": RandomForestClassifier(n_estimators=200, random_state=42),
"Logistic Regression": LogisticRegression(max_iter=500, solver="liblinear"),
}
for name, model in models.items():
model.fit(X_train_scaled, y_train)
y_pred = model.predict(X_test_scaled)
print(f"{name}: {accuracy_score(y_test, y_pred):.3f}")
Model Performance
Random Forest came out on top with an accuracy of 84.5%, outperforming both KNN and Logistic Regression. It was able to capture the more complex relationships between features that simpler models struggled with.
Why Random Forest Won
None of the models performed badly, but they each had different strengths. KNN worked surprisingly well by comparing each round to similar situations from the past, although it becomes slower as the dataset grows. Logistic Regression was the easiest model to interpret, but its linear nature meant it couldn’t capture some of the more complex interactions in the game.
Random Forest ended up performing the best because Counter-Strike rounds aren’t decided by a single factor. Economy, score, player count, remaining time, and equipment all influence one another, and Random Forest was much better at learning those combinations than the other models.
If I continued this project, I’d experiment with hyperparameter tuning, cross-validation, and newer ensemble models like XGBoost or LightGBM to see how much further the accuracy could be pushed.
Evaluation
After comparing the different models, I took the best-performing one and looked beyond overall accuracy. A confusion matrix and feature importance scores helped answer two questions: where does the model make mistakes, and which features influence its predictions the most?
from sklearn.metrics import classification_report
best = models["Random Forest"]
y_pred = best.predict(X_test)
print(classification_report(y_test, y_pred, target_names=encoder.classes_))
The model performs well on both CT and T rounds, with a fairly even distribution of correct and incorrect predictions. That balance suggests it isn't biased toward one side, even though T rounds appear slightly more often in the dataset.
Economy difference turned out to be the strongest predictor, followed by time remaining and score difference. That lines up pretty well with how Counter-Strike is actually played: better weapons, more time, and stronger momentum usually translate into a better chance of winning the round.
Key Takeaways
One of the most interesting parts of this project was seeing how closely the model’s behavior matched real gameplay. Money turned out to be the biggest factor, which makes sense since a stronger economy usually means better weapons and utility. Features like score difference, remaining time, and player count also contributed, showing that no single statistic determines the outcome on its own.
What I Learned
This project was a fun way to combine one of my favorite games with machine learning. It reinforced how important feature engineering is, especially when working with real-world datasets where the raw data rarely tells the full story.
I also learned that building a good model isn’t just about chasing higher accuracy. Comparing different algorithms, understanding why one performs better than another, and interpreting the results were just as valuable as the prediction itself.