Skip to content

Commit

Permalink
HighScoreList can now read/write from remote gamercv.com high scores …
Browse files Browse the repository at this point in the history
…API for global high scores! example13.rb communicates against http://www.gamercv.com/games/1  .. release new gem.
  • Loading branch information
ippa committed Oct 25, 2009
1 parent 230a82c commit f8aaad5
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 51 deletions.
4 changes: 2 additions & 2 deletions chingu.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

Gem::Specification.new do |s|
s.name = %q{chingu}
s.version = "0.5.8.2"
s.version = "0.5.9"

s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
s.authors = ["ippa"]
s.date = %q{2009-10-22}
s.date = %q{2009-10-25}
s.description = %q{OpenGL accelerated 2D game framework for Ruby.
Builds on the awesome Gosu (Ruby/C++) which provides all the core functionality.
It adds simple yet powerful game states, prettier input handling, deployment safe asset-handling, a basic re-usable game object and automation of common task.}
Expand Down
49 changes: 38 additions & 11 deletions examples/example13.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
class Game < Chingu::Window
def initialize
super(640,400)
self.input = {:esc => :exit}
self.caption = "Example of Chingus HighScore class"
self.input = {:esc => :exit, :space => :remote_high_score, :a => :add}
self.caption = "Example of Chingus HighScore class. Press Space to go to fetch high scores remotely!"

PulsatingText.create("HIGH SCORES", :x => $window.width/2, :y => 50, :size => 70)
@title = PulsatingText.create("HIGH SCORES", :x => $window.width/2, :y => 50, :size => 70)

#
# Load a list from disk, defaults to "high_score_list.yml"
Expand All @@ -36,27 +36,54 @@ def initialize
end
end

create_text

# @high_score_list.save_to_file # Uncomment to save list to disk
end

def remote_high_score
game_objects.destroy_all

@title = PulsatingText.create("REMOTE WEBSERVICE HIGH SCORES", :x => $window.width/2, :y => 50, :size => 30)

#
# :game_id is the unique ID the game has on www.gamercv.com
# :user is the login for that specific game (set by owner)
# :password is the password for that specific game (set by owner)
#
# To read a high score list only :game_id is required
# To write to a high score list :user and :password is required as well
#
@high_score_list = HighScoreList.load_remote(:game_id => 1, :user => "chingu", :password => "chingu", :size => 10)
create_text
end

def add
data = {:name => "NEW", :score => 1600}
position = @high_score_list.add(data)
puts "Got position: #{position.to_s}"
create_text
end

def create_text
@score_texts ||= []
@score_texts.each { |text| text.destroy }

#
# Iterate through all high scores and create the visual represenation of it
#
@high_score_list.each_with_index do |high_score, index|
y = index * 25 + 100
Text.create(high_score[:name], :x => 200, :y => y, :size => 20)
Text.create(high_score[:score], :x => 400, :y => y, :size => 20)
@score_texts << Text.create(high_score[:name], :x => 200, :y => y, :size => 20)
@score_texts << Text.create(high_score[:score], :x => 400, :y => y, :size => 20)
end

5.times do
score = rand(20000)
puts "position for possible score #{score}: #{@high_score_list.position_by_score(score)}"
end

# @high_score_list.save # Uncomment to save list to disk
end

def update
super
self.caption = "FPS #{$window.fps} - game objects: #{game_objects.size}"
end
end

#
Expand Down
42 changes: 22 additions & 20 deletions examples/high_score_list.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
---
- :name: ABC
:score: 9260
- :name: ABC
:score: 9112
- :name: ABC
:score: 8660
- :name: ABC
:score: 8030
- :name: ABC
:score: 7802
- :name: ABC
:score: 6976
- :name: ABC
:score: 6718
- :name: ABC
:score: 6325
- :name: ABC
:score: 5632
- :name: ABC
:score: 4199
- :name: NEW
:score: 9971
- :name: NEW
:score: 9960
- :name: NEW
:score: 9912
- :name: NEW
:score: 9900
- :name: NEW
:score: 9885
- :name: NEW
:score: 9840
- :name: NEW
:score: 9839
- :name: NEW
:score: 9746
- :name: NEW
:score: 9742
- :name: NEW
:score: 9736
- :name: NEW
:score: 9706
2 changes: 1 addition & 1 deletion lib/chingu.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@
require_all "#{CHINGU_ROOT}/chingu"

module Chingu
VERSION = "0.5.8.2"
VERSION = "0.5.9"
end
127 changes: 110 additions & 17 deletions lib/chingu/high_score_list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,11 @@ module Chingu
#
# - Keeps a local YAML file with highscores, default highscores.yml in root game dir.
# - Add, delete, clear highscores
# - Iterate through highscores with simple Highscore#each
# - Iterate through highscores with simple HighScores#each
#
# Working with the high score list:
# ...as a local file requires gem 'yaml'
# ...as a remote gamercv.com resource requires gem 'crack' and 'rest_client'
#
class HighScoreList
attr_reader :file
Expand All @@ -34,10 +38,16 @@ class HighScoreList
# Create a new high score list with 0 entries
#
def initialize(options = {})
require 'yaml'
@file = options[:file] || "high_score_list.yml"
@size = options[:size] || 100
@sort_on = options[:sort_on] || :score

@user = options[:user]
@password = options[:password]
@game_id = options[:game_id]
@server = "http://api.gamercv.com"
@resource = nil

@high_scores = Array.new
end

Expand All @@ -46,29 +56,63 @@ def initialize(options = {})
# If no :file is given, HighScoreList tries to load from file "high_score_list.yml"
#
def self.load(options = {})
require 'yaml'
high_score_list = HighScoreList.new(options)
high_score_list.load
return high_score_list
end

def self.load_remote(options = {})
high_score_list = HighScoreList.new(options)
high_score_list.load_remote
return high_score_list
end

#
# Add a new high score to list.
# Adda a new high score to the local file
# 'data' is a hash of key/value-pairs that needs to contain at least the keys :name and :score
# Returns the position it got in the list, with 1 beeing the first positions
#
def add(data)
raise "No :name value in high score!" if data[:name].nil?
raise "No :score value in high score!" if data[:score].nil?
@resource ? add_remote(data) : add_local(data)
position_by_score(data[:score])
end
alias << add

#
# POSTs a new high score to the remote web service
#
def add_remote(data)
begin
@res = @resource.post({:high_score => data})
data = Crack::XML.parse(@res)
add_to_list(force_symbol_hash(data["high_score"]))
rescue RestClient::RequestFailed
puts "RequestFailed: couldn't add high score"
rescue RestClient::Unauthorized
puts "Unauthorized to add high score (check :user and :password arguments)"
end
end

@high_scores.push(data)
@high_scores.sort! { |a, b| b[@sort_on] <=> a[@sort_on] }
@high_scores = @high_scores[0..@size]

#
# Adds a new high score to local file
# Returns the position it got in the list, with 1 beeing the first positions
#
def add_local(data)
add_to_list(force_symbol_hash(data))
save_to_file
end

#
# Returns the position of full data-hash data entry, used internally
#
def position_by_data(data)
position = @high_scores.rindex(data)
position += 1 if position
end
alias << add


#
# Returns the position 'score' would get in among the high scores:
# @high_score_list.position_by_score(999999999) # most likely returns 1 for the number one spot
Expand All @@ -83,6 +127,45 @@ def position_by_score(score)
return nil
end

#
# Load data from previously specified @file
#
def load
@high_scores = YAML.load_file(@file) if File.exists?(@file)
@high_scores = @high_scores[0..@size]
end

#
# Load data from remove web service.
# Under the hood, this is accomplished through a simple REST-interface
# The returned XML-data is converted into a simple Hash (@high_scores), which is also returned from this method.
#
def load_remote
raise "You need to specify a Game_id to load a remote high score list" unless defined?(@game_id)
raise "You need to specify a User to load a remote high score list" unless defined?(@user)
raise "You need to specify a Password to load a remote high score list" unless defined?(@password)

require 'rest_client'
require 'crack/xml'
@resource = RestClient::Resource.new "#{@server}/games/#{@game_id}/high_scores", :user => @user, :password => @password, :timeout => 20, :open_timeout => 5

@high_scores.clear
begin
res = @resource.get
data = Crack::XML.parse(res)
if data["high_scores"]
data["high_scores"].each do |high_score|
@high_scores.push(force_symbol_hash(high_score))
end
end
rescue RestClient::ResourceNotFound
puts "Couldn't find Resource, did you specify a correct :game_id ?"
end

@high_scores = @high_scores[0..@size]
return @high_scores
end

#
# Direct access to invidual high scores
#
Expand All @@ -101,21 +184,31 @@ def each_with_index
@high_scores.each_with_index { |high_score, index| yield high_score, index }
end

#
# Load data from previously specified @file
#
def load
@high_scores = YAML.load_file(@file) if File.exists?(@file)
end

#
# Save high score data into previously specified @file
#
def save
def save_to_file
require 'yaml'
File.open(@file, 'w') do |out|
YAML.dump(@high_scores, out)
end
end

private

def add_to_list(data)
@high_scores.push(data)
@high_scores.sort! { |a, b| b[@sort_on] <=> a[@sort_on] }
@high_scores = @high_scores[0..@size]
end

def force_symbol_hash(hash)
symbol_hash = {}
hash.each_pair do |key, value|
symbol_hash[key.to_sym] = value
end
return symbol_hash
end

end
end

0 comments on commit f8aaad5

Please sign in to comment.