Posted on
Artificial Intelligence

Artificial Intelligence Player Analytics

Author
  • User
    linuxbash
    Posts by this author
    Posts by this author

Artificial Intelligence Player Analytics on Linux: A Bash-First Pipeline

What if you could answer “Which players will churn next week?” or “Who are my high-value players?” from your terminal? With a few open-source tools and a sprinkle of machine learning, you can go from raw game logs to actionable insights—without leaving Linux or paying for a black-box platform.

This post shows how to build a minimal, reproducible AI-driven player analytics workflow that’s:

  • Bash-friendly and Git-friendly

  • Cheap to run on any Linux box

  • Transparent and customizable

We’ll collect events, engineer features at the command line, train a simple segmentation model, and produce terminal-native reports you can automate with cron.

Why AI Player Analytics is worth your time

  • Better retention: Segment players by behavior and target interventions before they churn.

  • Smarter monetization: Identify whales vs. casuals and tune offers accordingly.

  • Design feedback loop: Find progression bottlenecks by correlating playtime, sessions, and drop-off.

  • Ops at terminal speed: Simple, auditable pipelines you can run locally or in CI.

What we’ll build

  • Input: JSONL event logs (session ends, purchases)

  • Processing: jq and Bash to aggregate per-player features

  • Model: K-Means clustering in Python to segment players

  • Output: CSVs, SQLite tables, and a terminal plot

  • Automation: A single script and a cron entry


1) Install prerequisites

Use your package manager of choice to install core tools. Then we’ll use pip for Python libraries.

  • Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y python3 python3-venv python3-pip jq sqlite3 git gnuplot
  • Fedora/RHEL/CentOS (dnf):
sudo dnf install -y python3 python3-pip jq sqlite git gnuplot
  • openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y python3 python3-pip jq sqlite3 git gnuplot

Create a virtual environment and install ML libs:

python3 -m venv .venv
. .venv/bin/activate
pip install --upgrade pip
pip install numpy pandas scikit-learn joblib

2) Capture and structure events

Assume your game/client/server emits events as one JSON object per line (JSONL). Keep PII out; prefer hashed IDs.

Example: game_events.jsonl

{"player_id":"p1","event":"session_end","session_secs":300,"ts":"2026-07-01T12:00:00Z"}
{"player_id":"p1","event":"purchase","purchase_usd":4.99,"ts":"2026-07-01T12:05:00Z"}
{"player_id":"p2","event":"session_end","session_secs":120,"ts":"2026-07-02T08:13:00Z"}
{"player_id":"p2","event":"session_end","session_secs":200,"ts":"2026-07-02T09:20:00Z"}
{"player_id":"p2","event":"purchase","purchase_usd":1.99,"ts":"2026-07-02T09:25:00Z"}

Tip:

  • Use ISO-8601 timestamps (UTC).

  • Prefer append-only logs (easy to sync and process).

  • Validate schema at ingest time.


3) Build features in Bash with jq

We’ll derive per-player features: sessions, total playtime, average session length, purchase count, and spend. The following command slurps all events, groups by player_id, computes aggregates, and emits CSV.

jq -s '
  def ses: map(select(.event=="session_end") | .session_secs);
  def pur: map(select(.event=="purchase") | .purchase_usd);
  group_by(.player_id)
  | map({
      player_id: .[0].player_id,
      sessions: (ses | length),
      total_playtime: (ses | add // 0),
      avg_session: (ses | if length>0 then (add / length) else 0 end),
      purchases: (pur | length),
      spend: (pur | add // 0)
    })
  | (["player_id","sessions","total_playtime","avg_session","purchases","spend"],
     (.[] | [ .player_id, .sessions, .total_playtime, .avg_session, .purchases, .spend ]))
  | @csv
' game_events.jsonl > features.csv

Peek at the result:

head -5 features.csv | column -s, -t

Optional: Store features in SQLite for easy querying.

tail -n +2 features.csv > features_noheader.csv
sqlite3 analytics.db <<'SQL'
.mode csv
CREATE TABLE IF NOT EXISTS player_features (
  player_id TEXT,
  sessions INTEGER,
  total_playtime REAL,
  avg_session REAL,
  purchases INTEGER,
  spend REAL
);
.import features_noheader.csv player_features
SQL

4) Train a first-pass AI model (K-Means segmentation)

We’ll standardize features and cluster players into 4 behavioral segments.

Create train_kmeans.py:

#!/usr/bin/env python3
import pandas as pd
from sklearn.preprocessing import StandardScaler
from sklearn.cluster import KMeans
import joblib

# Load engineered features
df = pd.read_csv("features.csv")
feature_cols = ["sessions", "total_playtime", "avg_session", "purchases", "spend"]
X = df[feature_cols].fillna(0)

# Scale features (important for K-Means)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Train K-Means
kmeans = KMeans(n_clusters=4, n_init=10, random_state=42)
df["cluster"] = kmeans.fit_predict(X_scaled)

# Persist artifacts
df[["player_id", "cluster"]].to_csv("segments.csv", index=False)
centers = pd.DataFrame(
    scaler.inverse_transform(kmeans.cluster_centers_),
    columns=feature_cols
)
centers["cluster"] = range(len(centers))
centers.to_csv("cluster_centers.csv", index=False)

joblib.dump(
    {"scaler": scaler, "model": kmeans, "features": feature_cols},
    "player_segmentation.joblib"
)

print("Wrote segments.csv, cluster_centers.csv, player_segmentation.joblib")

Run it:

. .venv/bin/activate
python train_kmeans.py

Import segments into SQLite and explore:

tail -n +2 segments.csv > segments_noheader.csv
sqlite3 analytics.db <<'SQL'
.mode csv
CREATE TABLE IF NOT EXISTS player_segments (
  player_id TEXT PRIMARY KEY,
  cluster INTEGER
);
.import segments_noheader.csv player_segments

.mode column
.headers on
SELECT s.cluster, COUNT(*) AS players,
       ROUND(AVG(f.spend),2) AS avg_spend,
       ROUND(AVG(f.total_playtime),1) AS avg_playtime
FROM player_segments s
JOIN player_features f USING(player_id)
GROUP BY s.cluster
ORDER BY avg_spend DESC;
SQL

Optional: a quick terminal scatterplot (sessions vs spend) colored by cluster using gnuplot.

Join features and segments to a TSV:

awk -F, 'NR==FNR{seg[$1]=$2; next} FNR==1{print "player_id\tsessions\tspend\tcluster"; next} {print $1"\t"$2"\t"$6"\t"seg[$1]}' segments.csv features.csv > features_with_clusters.tsv

Split per-cluster data:

for i in 0 1 2 3; do
  awk -F'\t' 'NR>1 && $4=='"$i"' {print $2, $3}' features_with_clusters.tsv > c$i.dat
done

Plot in ASCII (adjust terminal width/height as needed):

gnuplot -persist <<'GP'
set term dumb 120 30
set title "Sessions vs Spend by Cluster"
set xlabel "sessions"
set ylabel "spend (USD)"
plot 'c0.dat' with points title 'c0', \
     'c1.dat' with points title 'c1', \
     'c2.dat' with points title 'c2', \
     'c3.dat' with points title 'c3'
GP

5) Automate the pipeline

Create run_pipeline.sh to tie it all together:

#!/usr/bin/env bash
set -euo pipefail

LOG_DIR="${LOG_DIR:-.}"
DATA="${DATA:-game_events.jsonl}"
DB="${DB:-analytics.db}"

# 1) venv and deps
if [ ! -d .venv ]; then
  python3 -m venv .venv
fi
. .venv/bin/activate
pip install -q --upgrade pip
pip install -q numpy pandas scikit-learn joblib

# 2) Feature engineering
jq -s '
  def ses: map(select(.event=="session_end") | .session_secs);
  def pur: map(select(.event=="purchase") | .purchase_usd);
  group_by(.player_id)
  | map({
      player_id: .[0].player_id,
      sessions: (ses | length),
      total_playtime: (ses | add // 0),
      avg_session: (ses | if length>0 then (add / length) else 0 end),
      purchases: (pur | length),
      spend: (pur | add // 0)
    })
  | (["player_id","sessions","total_playtime","avg_session","purchases","spend"],
     (.[] | [ .player_id, .sessions, .total_playtime, .avg_session, .purchases, .spend ]))
  | @csv
' "$DATA" > features.csv

# 3) Train model
python train_kmeans.py

# 4) Load into SQLite
tail -n +2 features.csv > features_noheader.csv
tail -n +2 segments.csv > segments_noheader.csv
sqlite3 "$DB" <<'SQL'
.mode csv
CREATE TABLE IF NOT EXISTS player_features (
  player_id TEXT,
  sessions INTEGER,
  total_playtime REAL,
  avg_session REAL,
  purchases INTEGER,
  spend REAL
);
DELETE FROM player_features;
.import features_noheader.csv player_features

CREATE TABLE IF NOT EXISTS player_segments (
  player_id TEXT PRIMARY KEY,
  cluster INTEGER
);
DELETE FROM player_segments;
.import segments_noheader.csv player_segments
SQL

echo "[OK] Pipeline completed at $(date)"

Make executable and test:

chmod +x run_pipeline.sh
./run_pipeline.sh

Run nightly via cron:

crontab -e

Add:

0 2 * * * /path/to/run_pipeline.sh >> /var/log/player_analytics.log 2>&1

Real-world applications you can ship this week

  • Retention saves: Find clusters with high first-session time but low return sessions. Trigger a day-2 push/email with a relevant reward.

  • Monetization tuning: Identify clusters with moderate playtime but zero spend; test a gentler early-offer ramp or currency drip.

  • Difficulty smoothing: If a cluster shows falling session counts beyond level N, investigate spike points and experiment with dynamic difficulty.

  • UA feedback: Share cluster mix by campaign/source with marketing to allocate spend toward higher-LTV cohorts.


Conclusion and next steps

You’ve built a transparent, end-to-end AI analytics loop:

  • Bash and jq to transform raw events into features

  • Python to learn behavior segments

  • SQLite and gnuplot to explore results in your terminal

  • Cron to keep it humming

From here, consider:

  • Adding recency/frequency/monetary (RFM) features and day-7 retention labels

  • Training a churn classifier (logistic regression) using time-windowed features

  • Versioning data and models with Git and Makefiles

  • Shipping daily CSVs to your BI tool—or just keep it CLI-native

If this helped, try pointing it at a week of production logs and see what clusters pop out. Then close the loop: test one intervention per segment and measure impact.