Skip to content

Instantly share code, notes, and snippets.

@gshutler
Forked from karlseguin/gist:3016329
Created July 1, 2012 16:26
Show Gist options
  • Save gshutler/3028862 to your computer and use it in GitHub Desktop.
Save gshutler/3028862 to your computer and use it in GitHub Desktop.
File-based score data
require 'digest'
require 'json'
require 'fileutils'
$leaderboard_count = 5
$user_count = 200000
def username(i)
Digest::SHA1.hexdigest(i.to_s)
end
def create_file(*parts)
file = File.join(parts)
FileUtils.mkdir_p File.dirname(file)
FileUtils.touch file
end
(1..$leaderboard_count).map do |lb|
Thread.new do
(1..$user_count).map do |i|
name = username(i)
data = {:points => i * lb}
# Support scores up to 999,999,999,999,999,999,999,999.
score_str = data[:points].to_s.rjust(24, '0')
# Support users submitting one score per second.
time_str = Time.now.utc.strftime('%Y%m%d%H%M%S')
# Create user records in the style:
#
# temp/users/xxxx/xxxx/xxxx/xxxx/xxxx/xxxx/xxxx/xxxx/xxxx/xxxx/GAME/SCORE-TIME
#
# This allows us to easily get an individual's best scores without having
# too many users in any one directory.
create_file 'temp', 'users', name.scan(/.{4}/), lb.to_s, "#{score_str}-#{time_str}"
# Create leaderboard records in the style:
#
# temp/leaderboard/GAME/xxx/xxx/xxx/xxx/xxx/xxx/xxx/xxx/TIME-USER
#
# This allows us to easily get the highest scores for a given game and
# filter them easily by time.
create_file 'temp', 'leaderboard', lb.to_s, score_str.scan(/.{3}/), "#{time_str}-#{name}"
end
end
end.each(&:join)
require 'benchmark'
require 'fileutils'
require 'json'
# Users 40 characters
# Scores 24 characters
MONTH_AGO = (Time.now.utc - (60*60*24*28)).strftime('%Y%m%d%H%M%S')
WEEK_AGO = (Time.now.utc - (60*60*24*7)).strftime('%Y%m%d%H%M%S')
SCOREBOARD_SIZE = 10
PRINT = false
def get_leaderboards(root)
cache_file = File.join 'cache', root
if File.exist?(cache_file) and File.mtime(root) < File.mtime(cache_file)
puts "Using cache file #{cache_file}" if PRINT
return JSON.parse IO.read(cache_file)
end
leaderboards = determine_leaderboards root
puts "Writing cache file #{cache_file}" if PRINT
FileUtils.mkdir_p File.dirname(cache_file)
File.write(cache_file, leaderboards.to_json)
leaderboards
end
def determine_leaderboards(root)
all_time = []
past_month = []
past_week = []
# Do a depth-first traversal of the directory structure, diving into the
# highest named directory first as that will contain the highest possible
# scores.
Dir["#{root}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |dir|
Dir["#{dir}/*"].sort.reverse.each do |file|
# Inspect each file and add it to the appropriate leaderboard
# lists.
all_time << file if all_time.length < SCOREBOARD_SIZE
past_month << file if past_month.length < SCOREBOARD_SIZE and file > MONTH_AGO
past_week << file if past_week.length < SCOREBOARD_SIZE and file > WEEK_AGO
# If all the leaderboards are filled return early.
return all_time, past_month, past_week if past_week.length == SCOREBOARD_SIZE
end
end
end
end
end
end
end
end
# Return whatever scores we have managed to collect.
return all_time, past_month, past_week
end
Benchmark.bmbm do |bm|
bm.report 'leaderboards' do
(1..5).each do |lb|
puts "Getting leaderboards for #{lb}" if PRINT
lb_dir = File.join('temp', 'leaderboard', lb.to_s)
all_time, past_month, past_week = get_leaderboards lb_dir
next unless PRINT
puts 'All Time'
puts '--------'
all_time.each { |s| puts s }
puts ''
puts 'Past month'
puts '----------'
past_month.each { |s| puts s }
puts ''
puts 'Past week'
puts '---------'
past_week.each { |s| puts s }
puts ''
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment