Skip to content

Commit

Permalink
Game quality/excitement determination engine
Browse files Browse the repository at this point in the history
  • Loading branch information
EddM committed Oct 5, 2013
1 parent 431dd02 commit 8e4de3d
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 7 deletions.
8 changes: 8 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ task :run do
task.run
end

task :assess do
config = BSR::Config.new("#{File.dirname __FILE__}/config/database.json")
DataMapper::Logger.new($stdout, :debug)
DataMapper.setup(:default, "mysql://#{config.user}@#{config.host}/#{config.database}")
DataMapper.finalize
Game.all.each { |g| g.assess! }
end

namespace :db do
task :migrate do
require 'dm-migrations/migration_runner'
Expand Down
14 changes: 14 additions & 0 deletions config/migrations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,17 @@
drop_table :events
end
end

migration 3, :add_game_quality do
up do
modify_table :games do
add_column :quality, Integer
end
end

down do
modify_table :games do
drop_column :quality
end
end
end
1 change: 1 addition & 0 deletions lib/parse_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def parse_doc(doc)
puts "Fetching #{url}"
game.build_from_html open(url)
game.insert_into_db(key)
game.assess!
rescue => e
puts "*** Failed for #{key}"
puts e.inspect
Expand Down
120 changes: 113 additions & 7 deletions web/lib/game.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,18 @@ class Game
property :bbref_key, String
property :slug, String
property :date, DateTime
property :quality, Integer

has n, :events

attr_reader :players
attr_reader :players, :score

def build_from_html(src = nil)
@doc = Nokogiri::HTML(src)
@players = [[], []]
@events = []

parse
parse!
end

def insert_into_db(bbref_key)
Expand All @@ -32,7 +33,7 @@ def insert_into_db(bbref_key)
end
end

def parse
def parse!
period = 0
header = @doc.css('h1').text.split(" Play-By-Play, ")
@team0, @team1 = header[0].split(" at ")
Expand All @@ -45,6 +46,21 @@ def parse
end
end

def assess!
@score = 0
fetch_events
@score += 35 if overtime?
@score += 20 if close_game?
@score += 15 if high_shooting_pct?
@score += 20 if very_high_scorer?
high_scorers_bonus = (high_scorers * 5)
high_scorers_bonus = 10 if high_scorers_bonus > 10
@score += high_scorers_bonus

self.quality = @score
self.save
end

def parse_row(tr, period)
cells = tr.css('td')
timestamp = timestamp_to_integer(cells[0].content, period)
Expand Down Expand Up @@ -98,11 +114,11 @@ def player_by_element(element, team)
end

def to_json
team0 = self.events.all(:team => 0, :fields => [:player, :name], :unique => true).map { |p| Player.new(p.name, p.player, 0) }
team1 = self.events.all(:team => 1, :fields => [:player, :name], :unique => true).map { |p| Player.new(p.name, p.player, 1) }
fetch_events

team_strings = []
[team0, team1].each do |team|
players = [@team0_events.map { |p| Player.new(p.name, p.player, 0) }, @team1_events.map { |p| Player.new(p.name, p.player, 1) }]
players.each do |team|
str = team.map { |player| "\"#{player.id}\" : { \"name\" : \"#{player.name}\", \"team\" : #{player.team} }" }.join(",")
team_strings << "{ #{str} }"
end
Expand All @@ -115,7 +131,7 @@ def day
Time.new(date.year, date.month, date.day)
end

private
# private

def reb(timestamp, cell, team, type = :o)
player_link = cell.css('a')
Expand Down Expand Up @@ -185,4 +201,94 @@ def player_id(element)
element.attr('href').to_s.split("/")[-1].split(".")[0]
end

def fetch_events
unless @team0_events && @team1_events
@team0_events = self.events.all(:team => 0, :unique => true)
@team1_events = self.events.all(:team => 1, :unique => true)
end
end

def close_game?
team0_score = @team0_events.reduce(0) do |score, event|
score + value = case event.type.to_sym
when :ftm then 1
when :fgm then 2
when :fgm3 then 3
else
0
end
end

team1_score = @team1_events.reduce(0) do |score, event|
score + value = case event.type.to_sym
when :ftm then 1
when :fgm then 2
when :fgm3 then 3
else
0
end
end

(team0_score - team1_score).abs <= 5
end

def overtime?
@team0_events.any? { |e| e.time > 2880 }
end

def players_with_points
return @players_with_points if @players_with_points
values = {}

(@team0_events + @team1_events).each do |event|
value = case event.type.to_sym
when :ftm then 1
when :fgm2 then 2
when :fgm3 then 3
end

values[event.player] ||= 0
values[event.player] += value if value
end

@players_with_points = values
end

def most_points
players_with_points.values.max
end

def high_scorer?
most_points >= 30
end

def very_high_scorer?
most_points >= 40
end

def high_scorers
players_with_points.values.select { |pts| pts >= 30 }.size
end

def high_shooting_pct?
players_with_shooting_pct = {}

(@team0_events + @team1_events).each do |event|
type = event.type.to_sym
players_with_shooting_pct[event.player] ||= { :fga => 0, :fgm => 0, :pct => 0 }

players_with_shooting_pct[event.player][:fga] += 1 if type == :fga2 || type == :fga3
if type == :fgm2 || type == :fgm3
players_with_shooting_pct[event.player][:fgm] += 1
players_with_shooting_pct[event.player][:fga] += 1
end

if players_with_shooting_pct[event.player][:fgm] > 0 && players_with_shooting_pct[event.player][:fga] > 0
players_with_shooting_pct[event.player][:pct] = (players_with_shooting_pct[event.player][:fgm] / players_with_shooting_pct[event.player][:fga].to_f)
end
end

players_with_shooting_pct.values.any? { |v| v[:pct] >= 0.825 && v[:fga] >= 12 }
end

end

0 comments on commit 8e4de3d

Please sign in to comment.