Machine Coding Problem

Cricbuzz Scoreboard

maco30maco60macoAllreal-timeobserver-patternstrategy-patternthread-safety
Commonly Asked By:YahooPaytmHotstar

Functional Scope (In-Scope)

  • Match Hierarchy Structuring: Support structured Innings, Overs, and Ball records cleanly.
  • Dynamic Score Calculations: Automatically aggregate totals, wickets, and averages as balls are inputted.
  • Decoupled Observer Dashboard updates: Register and update multiple frontend, notifications, and analytics platforms dynamically.
  • Flexible Final Score Projection: Support multiple strategy-based calculations for forecasting match outcomes.
  • Ball Commentary Logs: Record commentary entries for each delivery.

Explicit Boundaries (Out-of-Scope)

  • No Intelligent Video Streaming Rendering: Does not stream media frames or process live video feeds.
  • No Ticket Sales / Gate Management: Strictly handles cricket match stats and commentators scoring inputs.

Thread-safe real-time scoring blueprints in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.locks.*;

enum OutcomeType {
    RUNS, WICKET, EXTRA_WIDE, EXTRA_NO_BALL, BYE
}

class Delivery {
    private final int over;
    private final int ball;
    private final String bowler;
    private final String batsman;
    private final int runs;
    private final OutcomeType type;
    private final String commentary;

    public Delivery(int over, int ball, String bowler, String batsman, int runs, OutcomeType type, String commentary) {
        this.over = over;
        this.ball = ball;
        this.bowler = bowler;
        this.batsman = batsman;
        this.runs = runs;
        this.type = type;
        this.commentary = commentary;
    }

    public int getOver() { return over; }
    public int getBall() { return ball; }
    public String getBowler() { return bowler; }
    public String getBatsman() { return batsman; }
    public int getRuns() { return runs; }
    public OutcomeType getType() { return type; }
    public String getCommentary() { return commentary; }
}

interface ProjectionStrategy {
    int projectFinalScore(int currentRuns, int ballsBowled, int totalBalls, int wickets);
}

class CurrentRunRateProjector implements ProjectionStrategy {
    @Override
    public int projectFinalScore(int currentRuns, int ballsBowled, int totalBalls, int wickets) {
        if (ballsBowled == 0) return 0;
        double crr = (double) currentRuns / ((double) ballsBowled / 6.0);
        return (int) Math.round(crr * (totalBalls / 6.0));
    }
}

class DefensiveMatchProjector implements ProjectionStrategy {
    @Override
    public int projectFinalScore(int currentRuns, int ballsBowled, int totalBalls, int wickets) {
        int remainingBalls = totalBalls - ballsBowled;
        int remainingOvers = remainingBalls / 6;
        int expectedRate = (wickets > 7) ? 4 : (wickets > 4) ? 6 : 8;
        return currentRuns + (remainingOvers * expectedRate);
    }
}

interface ScoreObserver {
    void onScoreUpdate(String matchId, int runs, int wickets, double overs, int projectedScore, String commentary);
}

class WebDashboardObserver implements ScoreObserver {
    @Override
    public void onScoreUpdate(String matchId, int runs, int wickets, double overs, int projectedScore, String commentary) {
        System.out.println(String.format("[Web Dashboard] Match: %s | Score: %d/%d (%.1f overs) | Projected Score: %d | Last Action: %s",
                matchId, runs, wickets, overs, projectedScore, commentary));
    }
}

class FantasySportsObserver implements ScoreObserver {
    @Override
    public void onScoreUpdate(String matchId, int runs, int wickets, double overs, int projectedScore, String commentary) {
        System.out.println(String.format("[Fantasy Platform] Syncing points for Match %s. Live score runs: %d", matchId, runs));
    }
}

class MatchScorecard {
    private final String matchId;
    private final int totalBalls = 300; // 50-over match
    private int totalRuns = 0;
    private int wickets = 0;
    private int ballsBowled = 0;
    private final List<Delivery> deliveries = new ArrayList<>();
    private final List<ScoreObserver> observers = new CopyOnWriteArrayList<>();
    private ProjectionStrategy projectionStrategy;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();

    public MatchScorecard(String matchId, ProjectionStrategy initialStrategy) {
        this.matchId = matchId;
        this.projectionStrategy = initialStrategy;
    }

    public void addObserver(ScoreObserver obs) {
        observers.add(obs);
    }

    public void removeObserver(ScoreObserver obs) {
        observers.remove(obs);
    }

    public void setProjectionStrategy(ProjectionStrategy strategy) {
        lock.writeLock().lock();
        try {
            this.projectionStrategy = strategy;
        } finally {
            lock.writeLock().unlock();
        }
    }

    public void recordBall(int over, int ball, String bowler, String batsman, int runs, OutcomeType type, String extraCommentary) {
        lock.writeLock().lock();
        try {
            int runsScored = runs;
            if (type == OutcomeType.EXTRA_WIDE || type == OutcomeType.EXTRA_NO_BALL) {
                runsScored += 1;
            }
            if (type == OutcomeType.WICKET) {
                wickets++;
            }
            totalRuns += runsScored;
            if (type != OutcomeType.EXTRA_WIDE && type != OutcomeType.EXTRA_NO_BALL) {
                ballsBowled++;
            }

            String commentary = String.format("Over %d.%d: %s to %s -> %s (Runs: %d)", 
                over, ball, bowler, batsman, type, runsScored);
            if (extraCommentary != null && !extraCommentary.isEmpty()) {
                commentary += " | " + extraCommentary;
            }

            Delivery delivery = new Delivery(over, ball, bowler, batsman, runsScored, type, commentary);
            deliveries.add(delivery);

            double overs = (ballsBowled / 6) + ((ballsBowled % 6) / 10.0);
            int projected = projectionStrategy.projectFinalScore(totalRuns, ballsBowled, totalBalls, wickets);

            notifyObservers(overs, projected, commentary);
        } finally {
            lock.writeLock().unlock();
        }
    }

    private void notifyObservers(double overs, int projected, String commentary) {
        for (ScoreObserver obs : observers) {
            obs.onScoreUpdate(matchId, totalRuns, wickets, overs, projected, commentary);
        }
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("=== Cricbuzz Scoreboard Concurrent Simulation ===");
        MatchScorecard scorecard = new MatchScorecard("IND-vs-AUS-2026", new CurrentRunRateProjector());
        
        ScoreObserver dashboard = new WebDashboardObserver();
        ScoreObserver fantasy = new FantasySportsObserver();
        scorecard.addObserver(dashboard);
        scorecard.addObserver(fantasy);

        ExecutorService executor = Executors.newFixedThreadPool(2);

        executor.submit(() -> {
            scorecard.recordBall(1, 1, "Mitchell Starc", "Rohit Sharma", 4, OutcomeType.RUNS, "Stunning drive!");
            scorecard.recordBall(1, 2, "Mitchell Starc", "Rohit Sharma", 0, OutcomeType.WICKET, "Bowled him!");
        });

        executor.submit(() -> {
            try { Thread.sleep(50); } catch (InterruptedException ignored) {}
            scorecard.recordBall(1, 3, "Mitchell Starc", "Virat Kohli", 1, OutcomeType.RUNS, "Quick single");
        });

        executor.shutdown();
        executor.awaitTermination(2, TimeUnit.SECONDS);

        System.out.println("\n--- Switching Scorecard to Defensive Projection Strategy ---");
        scorecard.setProjectionStrategy(new DefensiveMatchProjector());
        scorecard.recordBall(1, 4, "Mitchell Starc", "Shubman Gill", 6, OutcomeType.RUNS, "Massive pull shot!");
    }
}