Machine Coding Problem

Cricbuzz Scoreboard

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

Requirements & System Scope

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.

Class Diagram & Entity Relationships

Structural layout showing relationships between match scorecard elements, projection strategies, and score update observers:

Loading...
  • Polymorphic Observers: Dashboards and external alert systems register via a unified ScoreObserver contract, letting notifications run smoothly.
  • Strategy Pattern for Projections: The scorecard calculates projected final scores dynamically using pluggable projection algorithms.

Design Patterns & SOLID Principles

  • Observer Pattern (Scoreboard updates): Decoupling the scoring engine from live presentation dashboards satisfies OCP, enabling developers to attach new dashboards without modifying core scorecard components.
  • Strategy Pattern (Dynamic Projections): Encapsulates scoring projection algorithms (e.g., current run-rate extrapolation vs defensive scenario modeling) into dedicated interchangeable classes.
  • Single Responsibility Principle (SRP): Scorecard tracks and aggregates live deliveries; ProjectionStrategy focuses solely on mathematical projection models; Observers process and render live updates.

Core Execution Workflows

Ball Score Aggregator Pipeline

  1. Scorer inputs details: recordBall(over, ball, bowler, batsman, runs, type, extraCommentary).
  2. Instantiate new Delivery object and push to delivery history array.
  3. Acquire exclusive scorecard lock:
    1. If outcome is a Wicket, increment wickets counter.
    2. Otherwise, parse runs and increment total runs (accounting for extra runs like Wide or No Ball).
  4. Execute the selected ProjectionStrategy to estimate final innings score.
  5. Loop over registered observers, dispatching scoring updates dynamically.

Concurrency & Thread Safety Strategy

When thousands of users poll scorecards while scorers enter delivery outcomes in real-time, concurrency issues arise:

  • Read-Write Locks (ReentrantReadWriteLock): Allow high-throughput concurrent reads of match statistics while ensuring that recording a ball has exclusive write-lock protection to keep statistics consistent.
  • Copy-On-Write Observers: Manage observer registers using safe collection wrappers (CopyOnWriteArrayList) to prevent thread collision during updates.

Complete Clean Code Blueprint

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!");
    }
}

๐Ÿ’ฌReview

Help Us Improve

How helpful was this walkthrough?

Click a star to rate. We actively use this feedback to refine and update our system design content.

Placeholder
Optional but highly appreciated!

Discussion

Share your thoughts, ask questions, or help others.

Loading comments...