Requirements - Problem: Design a cricket scorecard that will show the score for a team along with score of each player.
You will be given the number of players in each team, the number of overs and their batting order as input.
Then, we can input overs ball by ball with the runs scored on that ball (could be wide, no ball or a wicket as well).
You are expected to print individual scores, number of balls faced, number of 4s, number of 6s for all the players from the batting side at the end of every over.
You also need to print total score, total wickets. Essentially, you need to keep a track of all the players, strike changes (at the end of the over or after taking singles or 3s) and maintain their scores, also keep track of extra bowls that are being bowled (like wides or no balls).
You also need to print which team won the match at the end. This is the bare minimum solution which is expected for the problem.
You can add some more features once you are done with these, like maintaining a bowlers record (total overs bowled, runs conceded, wickets taken, maiden overs, dot balls, economy, etc.).
Total team extras, batsman strike rates, etc. can be added too. But these are "good to have" features, please try to complete the bare minimum first.
Make sure your code is readable and maintainable and preferably object oriented. It should be modular and extensible, to add new features if needed.
Sample input and output: No. of players for each team: 5 No. of overs: 2 Batting Order for team 1: P1 P2 P3 P4 P5 Over 1: 1 1 1 1 1 2
Scorecard for Team 1: Player Name Score 4s 6s Balls P1* 3 0 0 3 P2* 4 0 0 3 P3 0 0 0 0 P4 0 0 0 0 P5 0 0 0 0 Total: 7/0 Overs: 1
Over 2: W 4 4 Wd W 1 6
Scorecard for Team 1: Player Name Score 4s 6s Balls P1 3 0 0 4 P2* 10 0 1 4 P3 8 2 0 3 P4* 1 0 0 1 P5 0 0 0 0 Total: 23/2 Overs: 2
Batting Order for team 2: P6 P7 P8 P9 P10
Over 1: 4 6 W W 1 1
Scorecard for Team 2: Player Name Score 4s 6s Balls P6 10 1 1 3 P7* 1 0 0 1 P8 0 0 0 1 P9* 1 0 0 1 P10 0 0 0 0 Total: 12/1 Overs: 1
Over 2: 6 1 W W
Scorecard for Team 2: Player Name Score 4s 6s Balls P6 10 1 1 2 P7* 8 0 1 3 P8 0 0 0 1 P9 1 0 0 2 P10 0 0 0 1 Total: 19/4 Overs: 1.4
Result: Team 1 won the match by 4 runs
Design Patterns
1. Factory Pattern
The Factory Pattern can be used to create Team
or Player
objects, especially when initializing teams from an external source or when there are different types of players (e.g., batsman, bowler).
Implementation:
Add a PlayerFactory
or TeamFactory
class to handle object creation.
public class PlayerFactory {
public static Player createPlayer(String name) {
return new Player(name);
}
}
public class TeamFactory {
public static Team createTeam(String name, List<String> battingOrder) {
List<Player> players = new ArrayList<>();
for (String playerName : battingOrder) {
players.add(PlayerFactory.createPlayer(playerName));
}
return new Team(name, players);
}
}
Team team1 = TeamFactory.createTeam(team1Name, team1BattingOrder);
2. Strategy Pattern
The Strategy Pattern can handle different scoring rules (e.g., T20, ODI, or Test matches), which may have unique rules for extras or scoring. For example, wides and no-balls may have different penalties in various formats.
Implementation:
Define a ScoringStrategy
interface and implement different scoring rules.
public interface ScoringStrategy {
void processBall(Team team, String ballInput);
}
public class T20ScoringStrategy implements ScoringStrategy {
@Override
public void processBall(Team team, String ballInput) {
// Implement T20-specific rules
}
}
public class TestScoringStrategy implements ScoringStrategy {
@Override
public void processBall(Team team, String ballInput) {
// Implement Test-specific rules
}
}
private ScoringStrategy scoringStrategy;
public void setScoringStrategy(ScoringStrategy scoringStrategy) {
this.scoringStrategy = scoringStrategy;
}
// Delegate scoring logic
public void processBall(String ballInput) {
scoringStrategy.processBall(currentBattingTeam, ballInput);
}
3. Observer Pattern
The Observer Pattern can track and notify changes in team statistics (e.g., updating the scorecard, notifying the UI). The Team
class acts as a subject, and scorecard or other components act as observers.
Implementation:
Define an observer interface:
public interface ScoreObserver {
void updateScore(Team team);
}
public class ConsoleScoreDisplay implements ScoreObserver {
@Override
public void updateScore(Team team) {
team.printScorecard();
}
}
private List<ScoreObserver> observers = new ArrayList<>();
public void addObserver(ScoreObserver observer) {
observers.add(observer);
}
private void notifyObservers() {
for (ScoreObserver observer : observers) {
observer.updateScore(this);
}
}
// Notify after each ball
public void processBall(String input) {
// Process ball logic...
notifyObservers();
}
4. Singleton Pattern
Use the Singleton Pattern for shared components like a MatchManager to manage global configurations, match state, or logging.
public class MatchManager {
private static volatile MatchManager instance;
private MatchManager() { }
public static MatchManager getInstance() {
if (instance == null) { // First check (without locking)
synchronized (MatchManager.class) {
if (instance == null) { // Second check (with locking)
instance = new MatchManager();
}
}
}
return instance;
}
public void log(String message) {
System.out.println("[MatchManager]: " + message);
}
}
5. Template Method Pattern
If you want to handle innings differently for different match types, use the Template Method Pattern. For example, implement playInnings
as a template method with steps defined in the base class and overridden by specific match types.
public abstract class Match {
public final void playInnings(Team battingTeam) {
initializeInnings();
for (int i = 0; i < getOvers(); i++) {
playOver(battingTeam, i + 1);
}
finalizeInnings();
}
protected abstract void initializeInnings();
protected abstract void playOver(Team battingTeam, int overNumber);
protected abstract void finalizeInnings();
}
Subclass
public class T20Match extends Match {
@Override
protected void initializeInnings() {
System.out.println("Starting T20 innings...");
}
@Override
protected void playOver(Team battingTeam, int overNumber) {
System.out.println("Playing T20 over " + overNumber);
}
@Override
protected void finalizeInnings() {
System.out.println("End of T20 innings.");
}
}
6. Command Pattern
For extensibility, the Command Pattern can manage actions like adding runs, changing strike, or recording a wicket. Each action becomes a command, making it easier to undo/redo operations.
Command Interface:
public interface Command {
void execute();
void undo();
}
public class AddRunsCommand implements Command {
private Player player;
private int runs;
public AddRunsCommand(Player player, int runs) {
this.player = player;
this.runs = runs;
}
@Override
public void execute() {
player.addRuns(runs);
}
@Override
public void undo() {
player.addRuns(-runs);
}
}
private Stack<Command> commandStack = new Stack<>();
public void processCommand(Command command) {
command.execute();
commandStack.push(command);
}