Last active
June 26, 2017 08:36
-
-
Save akshaysasidrn/42014dfeb5e9cc80ecc9c8319677ded1 to your computer and use it in GitHub Desktop.
Cricket simulation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module CricketSimulation | |
WIN = :win | |
LOSE = :lose | |
TIE = :tie | |
# Team class holds the team attributes. | |
class Team | |
attr_accessor :teamname, :player_line_up, :first_batting, :result | |
def initialize(team_name, players) | |
@teamname = team_name | |
@player_line_up = players | |
@first_batting = false | |
set_player_team(players) | |
end | |
def set_player_team(players) | |
players.each do |player| | |
player.team = self | |
end | |
end | |
def score | |
self.player_line_up.map(&:score).reduce(:+) | |
end | |
end | |
# Player class holds the player attributes. | |
class Player | |
attr_accessor :name, :team, :balls, :score | |
def initialize(name, stats) | |
@name = name | |
raise "Stats: Probability distribution must add up to 1" if stat_summation_invalid?(stats) | |
@stats = stats | |
@balls = 0 | |
@score = 0 | |
end | |
def stat_summation_invalid?(stats) | |
stats.values.reduce(:+).round(2) != 1.0 | |
end | |
def bat | |
result = @stats.max_by {|_, weight| rand ** (1.0 / weight)}.first | |
if result.is_a? Integer | |
@balls += 1 | |
@score += result | |
end | |
return result | |
end | |
end | |
# Umpire class makes all the decisions regarding the match. | |
class Umpire | |
attr_accessor :player_line_up | |
def initialize(match_details, commentator) | |
@match_details = match_details | |
@commentator = commentator | |
@end_game = false | |
end | |
def decrement_over | |
@match_details.overs -= 1 | |
end | |
def switch_player | |
@match_details.active_player = @match_details.inactive_player | |
end | |
def process_output(output) | |
return {active: @match_details.inactive_player, score: output} if [1, 3, 5].include? output | |
return {active: @match_details.active_player, score: output} if [0, 2, 4, 6, :out].include? output | |
end | |
def player_out | |
out = @match_details.in_crease.shift # active player leaves crease | |
@match_details.in_crease.unshift(@match_details.player_line_up.shift) unless @match_details.wickets_over? # new lined up player enters | |
return out | |
end | |
def ends_game? | |
@end_game | |
end | |
def set_win(ball) | |
@end_game = true | |
unless @match_details.first_batting? | |
@match_details.set_result(WIN) | |
@commentator.win(@match_details.overs, ball, @match_details.players, @match_details.wicket_pending) | |
end | |
end | |
def set_defeat(ball) | |
@end_game = true | |
unless @match_details.first_batting? | |
@match_details.set_result(LOSE) | |
@commentator.lose(@match_details.overs, ball, @match_details.players, @match_details.to_win) | |
end | |
end | |
def set_tie | |
@end_game = true | |
unless @match_details.first_batting? | |
@match_details.set_result(TIE) | |
@commentator.tie | |
end | |
end | |
def end_game(result, ball) | |
if result == :win | |
set_win(ball) | |
elsif result == :lose | |
set_defeat(ball) | |
else | |
@match_details.to_win == 1 ? set_tie : set_defeat(ball) | |
end | |
end | |
def outlook_over(over, ball) | |
output = @match_details.active_batsman.bat | |
result = process_output(output) | |
determine_decision(result, over, ball) | |
end | |
def determine_decision(result, over, ball) | |
if result[:score] == :out | |
player = player_out | |
@commentator.out(player.name, over, ball) | |
end_game(:lose, ball) if @match_details.wickets_over? | |
else | |
@match_details.to_win -= result[:score] unless @match_details.first_batting? | |
@commentator.realtime_info(@match_details.active_batsman.name, result[:score], over, ball) | |
end_game(:win, ball) if @match_details.to_win <= 0 && !@match_details.first_batting? | |
end | |
@match_details.active_player = result[:active] | |
end | |
end | |
# MatchDetails class holds and handles the information regarding the match. | |
class MatchDetails | |
attr_accessor :overs, :to_win, :player_line_up, :players, :in_crease, :active_player | |
def initialize(overs, team, to_win) | |
@overs = overs | |
@to_win = to_win | |
set_team(team) | |
@player_line_up = batting_team.player_line_up.dup | |
@players = @player_line_up.dup | |
@in_crease = @player_line_up.shift(2) | |
@active_player = 0 | |
end | |
def set_team(team) | |
@@teams ||= [] | |
@@teams << team | |
end | |
def teams | |
@@teams | |
end | |
def inactive_player | |
@active_player == 0 ? 1 : 0 | |
end | |
def active_batsman | |
@in_crease[@active_player] | |
end | |
def wickets_over? | |
true if @player_line_up.empty? && @in_crease.count == 1 | |
end | |
def wicket_pending | |
@player_line_up.count | |
end | |
def first_batting? | |
batting_team.first_batting | |
end | |
def batting_team | |
teams.last | |
end | |
def bowling_team | |
(teams - [batting_team]).first | |
end | |
def set_result(status) | |
case status | |
when WIN | |
batting_team.result = WIN | |
bowling_team.result = LOSE if teams.count > 1 | |
when LOSE | |
batting_team.result = LOSE | |
bowling_team.result = WIN if teams.count > 1 | |
when TIE | |
batting_team.result = TIE | |
bowling_team.result = TIE if teams.count > 1 | |
else | |
puts "invalid status" | |
end | |
end | |
def result | |
result = {} | |
teams.each {|team| result[team.teamname] = team.result} | |
return result | |
end | |
end | |
# Cricket class handles the simulation of the match | |
class Cricket | |
attr_accessor :match_details | |
def initialize(overs, team, to_win, commentator) | |
@match_details = MatchDetails.new(overs, team, to_win) | |
@umpire = Umpire.new(@match_details, commentator) | |
@commentator = commentator | |
end | |
def simulate_match | |
@match_details.overs.times do |over| | |
@commentator.over_sum_up(@match_details.overs, @match_details.to_win) | |
simulate_over(over) | |
@umpire.decrement_over | |
return if @umpire.ends_game? | |
end | |
@umpire.end_game(:overs_up, 6) | |
end | |
def simulate_over(over) | |
1.upto(6) do |ball| | |
break if @umpire.ends_game? | |
@umpire.outlook_over(over, ball) | |
end | |
@umpire.switch_player | |
end | |
def self.simulate_tie_breaker(overs, team, to_win, commentator) | |
commentator.innings(team) | |
team_game = Cricket.new(overs, team, to_win, commentator) | |
team_game.simulate_match | |
team_score = team.score | |
return team_score | |
end | |
def self.tie_breaker_game(team1, team2, overs) | |
commentator = Commentator.new | |
commentator.tie_breaker = true | |
team1_score = Cricket.simulate_tie_breaker(overs, team1, 0, commentator) | |
team2_score = Cricket.simulate_tie_breaker(overs, team2, team1_score + 1, commentator) | |
commentator.tie_breaker_summary([team1, team2]) | |
end | |
end | |
# Commentator class deals with printing out comments. | |
class Commentator | |
attr_accessor :tie_breaker | |
def over_sum_up(over, to_win) | |
puts "\n#{over} overs left. #{to_win} runs to win" unless @tie_breaker | |
end | |
def realtime_info(player_name, score, over, ball) | |
puts "#{over}.#{ball} #{player_name} scores #{score} run" | |
end | |
def out(player_name, over, ball) | |
puts "#{over}.#{ball} #{player_name} is out" | |
end | |
def win(overs_left, balls_thrown, players, wicket_pending) | |
remaining_balls = CommentHelper.calc_remaining_balls(balls_thrown, overs_left) | |
puts "\n#{players.first.team.teamname} won by #{wicket_pending} wickets and #{remaining_balls} balls remaining" | |
player_scores(players) unless @tie_breaker | |
end | |
def lose(overs_left, balls_thrown, players, to_win) | |
remaining_balls = CommentHelper.calc_remaining_balls(balls_thrown, overs_left) | |
puts "\n#{players.first.team.teamname} lost by #{to_win} runs and #{remaining_balls} balls remaining" | |
player_scores(players) unless @tie_breaker | |
end | |
def tie | |
puts "\nThe match has resulted in a tie!" | |
end | |
def tie_breaker_summary(teams) | |
teams.each do |team| | |
puts "\n#{team.teamname}" | |
player_scores(team.player_line_up) | |
end | |
end | |
def innings(team) | |
puts "\n#{team.teamname} innings:" | |
end | |
def player_scores(players) | |
players.each do |player| | |
puts "#{player.name} - #{player.score}(#{player.balls})" | |
end | |
end | |
end | |
# CommentHelper class holds helper methods for Commentator class. | |
class CommentHelper | |
def self.calc_remaining_balls(balls_thrown, overs_left) | |
remaining_balls = (6 - balls_thrown) | |
remaining_balls += (overs_left - 1 ) * 6 unless overs_left.zero? | |
return remaining_balls | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "cricket_simulation" | |
require "test/unit" | |
include CricketSimulation | |
TEAM1 = "Outer-Heaven" | |
PLAYER1 = "Solid Snake" | |
PLAYER2 = "Liquid Snake" | |
TEAM2 = "Konoha" | |
PLAYER3 = "Uzumaki Naruto" | |
PLAYER4 = "Hatake Kakashi" | |
class PlayerTest < Test::Unit::TestCase | |
def test_player_create | |
p1 = Player.new(PLAYER1, { | |
0 => 0.05, | |
1 => 0.30, | |
2 => 0.25, | |
3 => 0.10, | |
4 => 0.15, | |
5 => 0.01, | |
6 => 0.09, | |
:out => 0.05 | |
}) | |
assert_equal PLAYER1, p1.name | |
end | |
def test_probabilty_distribution_validity | |
assert_raises RuntimeError do | |
p1 = Player.new(PLAYER2, { | |
0 => 1, | |
1 => 2, | |
2 => 3, | |
3 => 4, | |
4 => 5, | |
5 => 6, | |
6 => 7, | |
:out => 8 | |
}) | |
end | |
end | |
end | |
class TeamTest < Test::Unit::TestCase | |
def test_team_create | |
p1 = Player.new(PLAYER1, { | |
0 => 0.05, | |
1 => 0.30, | |
2 => 0.25, | |
3 => 0.10, | |
4 => 0.15, | |
5 => 0.01, | |
6 => 0.09, | |
:out => 0.05 | |
}) | |
team = Team.new(TEAM1, [p1]) | |
assert_equal PLAYER1, p1.name | |
assert_equal team, p1.team | |
end | |
end | |
# stats given probability 1, will only occur indefinitely. Pretty cool to test. | |
class UmpireTest < Test::Unit::TestCase | |
def test_change_player_on_over | |
puts "\n\nTEST: PLAYER CHANGE ON OVER" | |
p1 = Player.new(PLAYER1, { | |
0 => 1, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 0, | |
1 => 0.3, | |
2 => 0, | |
3 => 0.3, | |
4 => 0, | |
5 => 0.4, | |
6 => 0, | |
:out => 0 | |
}) | |
player_line_up = [p1, p2] | |
team = Team.new(TEAM1, player_line_up) | |
team.first_batting = true | |
commentator = Commentator.new | |
game = Cricket.new(2, team, 40, commentator) | |
game.simulate_over(0) | |
assert_equal PLAYER2, game.match_details.active_batsman.name | |
end | |
def test_change_player_on_odd_score | |
puts "\n\nTEST: PLAYER CHANGE ON ODD SCORES" | |
p1 = Player.new(PLAYER1, { | |
0 => 1, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 0, | |
1 => 0.3, | |
2 => 0, | |
3 => 0.3, | |
4 => 0, | |
5 => 0.4, | |
6 => 0, | |
:out => 0 | |
}) | |
player_line_up = [p1, p2] | |
team = Team.new(TEAM1, player_line_up) | |
team.first_batting = true | |
commentator = Commentator.new | |
game = Cricket.new(2, team, 40, commentator) | |
game.simulate_over(0) | |
assert_equal PLAYER2, game.match_details.active_batsman.name | |
game.simulate_over(1) | |
assert_equal PLAYER2, game.match_details.active_batsman.name | |
end | |
def test_out_of_balls | |
puts "\n\nTEST SCENARIO: OUT OF BALLS" | |
p1 = Player.new(PLAYER1, { | |
0 => 1, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 1, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
player_line_up = [p1, p2] | |
team = Team.new(TEAM1, player_line_up) | |
team.first_batting = true | |
commentator = Commentator.new | |
game = Cricket.new(4, team, 40, commentator) | |
game.simulate_match | |
assert_equal 0, game.match_details.teams.last.score | |
end | |
def test_run_out | |
puts "\n\nTEST SCENARIO: RUN OUT" | |
p1 = Player.new(PLAYER1, { | |
0 => 0, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 1 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 0, | |
1 => 0, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 1 | |
}) | |
player_line_up = [p1, p2] | |
commentator = Commentator.new | |
team = Team.new(TEAM1, player_line_up) | |
team.first_batting = true | |
game = Cricket.new(4, team, 40, commentator) | |
game.simulate_match | |
assert_equal 0, game.match_details.wicket_pending | |
end | |
def test_tie | |
puts "\n\nTEST SCENARIO: TIE" | |
p1 = Player.new(PLAYER1, { | |
0 => 0, | |
1 => 1, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 0, | |
1 => 1, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
player_line_up = [p1, p2] | |
commentator = Commentator.new | |
team = Team.new(TEAM1, player_line_up) | |
game = Cricket.new(4, team, 25, commentator) | |
game.simulate_match | |
assert_equal TIE, game.match_details.result[TEAM1] | |
end | |
def test_win | |
puts "\n\nTEST SCENARIO: WIN" | |
p1 = Player.new(PLAYER1, { | |
0 => 0, | |
1 => 1, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
p2 = Player.new(PLAYER2, { | |
0 => 0, | |
1 => 1, | |
2 => 0, | |
3 => 0, | |
4 => 0, | |
5 => 0, | |
6 => 0, | |
:out => 0 | |
}) | |
player_line_up = [p1, p2] | |
team = Team.new(TEAM1, player_line_up) | |
commentator = Commentator.new | |
game = Cricket.new(4, team, 5, commentator) | |
game.simulate_match | |
assert_equal WIN, game.match_details.result[TEAM1] | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "cricket_simulation" | |
include CricketSimulation | |
# set-up | |
p1 = Player.new("Kirat Boli", { | |
0 => 0.05, | |
1 => 0.30, | |
2 => 0.25, | |
3 => 0.10, | |
4 => 0.15, | |
5 => 0.01, | |
6 => 0.09, | |
:out => 0.05 | |
}) | |
p2 = Player.new("N.S Nodhi", { | |
0 => 0.10, | |
1 => 0.40, | |
2 => 0.20, | |
3 => 0.05, | |
4 => 0.10, | |
5 => 0.01, | |
6 => 0.04, | |
:out => 0.10 | |
}) | |
p3 = Player.new("R Rumrah", { | |
0 => 0.20, | |
1 => 0.30, | |
2 => 0.15, | |
3 => 0.05, | |
4 => 0.05, | |
5 => 0.01, | |
6 => 0.04, | |
:out => 0.20 | |
}) | |
p4 = Player.new("Sashi Henra", { | |
0 => 0.30, | |
1 => 0.25, | |
2 => 0.05, | |
3 => 0.00, | |
4 => 0.05, | |
5 => 0.01, | |
6 => 0.04, | |
:out => 0.30 | |
}) | |
player_line_up = [p1, p2, p3, p4] | |
team = Team.new("Lengaburu", player_line_up) | |
commentator = Commentator.new | |
overs = 4 | |
to_win = 40 | |
game = Cricket.new(overs, team, to_win, commentator) | |
# simulation | |
game.simulate_match |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require_relative "cricket_simulation" | |
include CricketSimulation | |
# set-up | |
p1 = Player.new("Kirat Boli", { | |
0 => 0.05, | |
1 => 0.10, | |
2 => 0.25, | |
3 => 0.10, | |
4 => 0.25, | |
5 => 0.01, | |
6 => 0.14, | |
:out => 0.10 | |
}) | |
p2 = Player.new("N.S Nodhi", { | |
0 => 0.05, | |
1 => 0.15, | |
2 => 0.15, | |
3 => 0.10, | |
4 => 0.20, | |
5 => 0.01, | |
6 => 0.19, | |
:out => 0.15 | |
}) | |
team1 = Team.new("Lengaburu", [p1, p2]) | |
team1.first_batting = true | |
p3 = Player.new("DB Vellyers", { | |
0 => 0.05, | |
1 => 0.10, | |
2 => 0.25, | |
3 => 0.10, | |
4 => 0.25, | |
5 => 0.01, | |
6 => 0.14, | |
:out => 0.10 | |
}) | |
p4 = Player.new("H Mamia", { | |
0 => 0.10, | |
1 => 0.15, | |
2 => 0.15, | |
3 => 0.10, | |
4 => 0.20, | |
5 => 0.01, | |
6 => 0.19, | |
:out => 0.10 | |
}) | |
team2 = Team.new("Enchai", [p3, p4]) | |
overs = 1 | |
Cricket.tie_breaker_game(team1, team2, overs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment