sanichi-chess_icu 0.2.1 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -13,38 +13,30 @@ For parsing files of chess tournament data into ruby classes.
13
13
  A tournament (ICU::Tournament) has two or more players (ICU::Player), and each player has one or more results (ICU::Result).
14
14
 
15
15
  Tournament objects are created by parsing files of various formats. An instance of a class that can handle the format
16
- (see below for the available classes) is first created, and then it's parse or parse! method is called with the file's
17
- contents as argument.
16
+ (see below for the available classes) is first created, and then it's <em>parse</em> or <em>parse!</em> method is called
17
+ with the file's contents as the only argument.
18
18
 
19
+ require 'chess_icu'
19
20
  data = open('tournament.csv') { |f| f.read }
20
21
  tournament = parser.parse(data)
21
22
 
22
- On success, the parse method returns an object of type ICU::Tournament. On error, it returns nil and an error message
23
+ On success, the parse method returns an object of type ICU::Tournament. On error, it returns _nil_ and an error message
23
24
  can be retrieved from the parser instance:
24
25
 
25
26
  parser.error # => error message or nil on success
26
27
 
27
- Alternatively, if an exception is preferred on error, the parse! method can be used:
28
+ Alternatively, if an exception is preferred on error, the <em>parse!</em> method can be used:
28
29
 
29
30
  tournament = parser.parse!(data)
30
31
 
31
32
  The file formats supported in the current version are:
32
33
 
33
- * Foreign CSV
34
+ * Foreign CSV (ICU::Tournament::ForeignCSV) - for players to report their individual results in foreign tournaments
34
35
 
35
36
 
36
- === Foreign CSV
37
-
38
- This is a format used by the ICU[http://icu.ie] ({specification}[http://www.icu.ie/articles/display.php?id=172])
39
- for players to submit their individual results in foreign tournaments for domestic rating.
40
- It's parsed by instances of ICU::Tournament::ForeignCSV.
41
-
42
- parser = ICU::Tournament::ForeignCSV.new
43
-
44
-
45
37
  == TODO
46
38
 
47
- Future versions of this software will be able to parse more common formats such as SwissPerfect and FIDE's Krause format.
39
+ Future versions of this software will be able to parse other types of files such as SwissPerfect and FIDE's Krause format.
48
40
 
49
41
 
50
42
  == Author
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :patch: 1
2
+ :patch: 4
3
3
  :major: 0
4
4
  :minor: 2
data/lib/result.rb CHANGED
@@ -2,6 +2,8 @@ module ICU
2
2
  class Result
3
3
  attr_reader :round, :player, :score, :colour, :opponent, :rateable
4
4
 
5
+ # Constructor. Round number, player number and score must be supplied.
6
+ # Optional hash attribute are _opponent_, _colour_ and _rateable_.
5
7
  def initialize(round, player, score, opt={})
6
8
  self.round = round
7
9
  self.player = player
@@ -25,7 +27,7 @@ module ICU
25
27
  raise "invalid player number (#{player})" if @player == 0 && !player.to_s.match(/\d/)
26
28
  end
27
29
 
28
- # Score for the game, even if a default. One of 'W', 'L' or 'D' (after some cleaning up).
30
+ # Score for the game, even if a default. One of 'W', 'L' or 'D'. Reasonable inputs like 1, 0, =, ½, etc will be converted.
29
31
  def score=(score)
30
32
  @score = case score.to_s.strip
31
33
  when /^(1\.0|1|\+|W|w)$/ then 'W'
@@ -35,7 +37,7 @@ module ICU
35
37
  end
36
38
  end
37
39
 
38
- # The score as a number.
40
+ # Return the score as a floating point number.
39
41
  def points
40
42
  case @score
41
43
  when 'W' then 1.0
@@ -44,7 +46,7 @@ module ICU
44
46
  end
45
47
  end
46
48
 
47
- # Colour. Either 'W' or 'B' after some cleaning up.
49
+ # Colour. Either 'W' (white) or 'B' (black).
48
50
  def colour=(colour)
49
51
  @colour = case colour.to_s
50
52
  when '' then nil
@@ -54,7 +56,7 @@ module ICU
54
56
  end
55
57
  end
56
58
 
57
- # Opponent player number. Either absent (nil) or any integer except the player number.
59
+ # Opponent player number. Either absent (_nil_) or any integer except the player number.
58
60
  def opponent=(opponent)
59
61
  @opponent = case opponent
60
62
  when nil then nil
@@ -67,7 +69,7 @@ module ICU
67
69
  self.rateable = true if @opponent
68
70
  end
69
71
 
70
- # Rateable flag. If false, game is not rateable. Can only be true if there is an opponent.
72
+ # Rateable flag. If false, result is not rateable. Can only be true if there is an opponent.
71
73
  def rateable=(rateable)
72
74
  if opponent.nil?
73
75
  @rateable = false
@@ -91,7 +93,7 @@ module ICU
91
93
  r
92
94
  end
93
95
 
94
- # Loose equality.
96
+ # Loose equality. True if the round, player and opponent numbers, colour and score all match.
95
97
  def ==(other)
96
98
  return unless other.is_a? Result
97
99
  [:round, :player, :opponent, :colour, :score].each do |m|
data/lib/tournament.rb CHANGED
@@ -1,8 +1,44 @@
1
1
  module ICU
2
+
3
+ =begin rdoc
4
+
5
+ == Generic Tournament
6
+
7
+ Normally a tournament object is created by parsing a data file (e.g. with ICU::Tournament::ForeignCSV).
8
+ However, it is also possible to build a tournament by first creating a bare tournament instance and then
9
+ firstly adding all the players and then adding all the results.
10
+
11
+ require 'rubygems'
12
+ require 'chess_icu'
13
+
14
+ t = ICU::Tournament.new('Bangor Masters', '2009-11-09')
15
+
16
+ t.add_player(ICU::Player.new('Bobby', 'Fischer', 10))
17
+ t.add_player(ICU::Player.new('Garry', 'Kasparov', 20))
18
+ t.add_player(ICU::Player.new('Mark', 'Orr', 30))
19
+
20
+ t.add_result(ICU::Result.new(1, 10, 'D', :opponent => 30, :colour => 'W'))
21
+ t.add_result(ICU::Result.new(2, 20, 'W', :opponent => 30, :colour => 'B'))
22
+ t.add_result(ICU::Result.new(3, 20, 'L', :opponent => 10, :colour => 'W'))
23
+
24
+ [10, 20, 30].each { |n| puts "#{t.player(n).points} #{t.player(n).name}" }
25
+
26
+ Would result in the following output.
27
+
28
+ 1.5 Bobby Fischer
29
+ 1.0 Gary Kasparov
30
+ 0.5 Mark Orr
31
+
32
+ Note that the players should be added first because the _add_result_ method will
33
+ raise an exception if the players it references through their numbers (10, 20
34
+ and 30 in this example) have not already been added to the tournament.
35
+
36
+ =end
37
+
2
38
  class Tournament
3
39
  attr_reader :name, :start, :rounds, :site
4
40
 
5
- # Constructor.
41
+ # Constructor. Name and start date must be supplied. Other attributes are optional.
6
42
  def initialize(name, start, opt={})
7
43
  self.name = name
8
44
  self.start = start
@@ -10,20 +46,20 @@ module ICU
10
46
  @player = {}
11
47
  end
12
48
 
13
- # Tournament name.
49
+ # Set the tournament name.
14
50
  def name=(name)
15
51
  raise "invalid tournament name (#{name})" unless name.to_s.match(/[a-z]/i)
16
52
  @name = name.to_s.strip
17
53
  end
18
54
 
19
- # Start data in yyyy-mm-dd format.
55
+ # Set a start date in yyyy-mm-dd format.
20
56
  def start=(start)
21
57
  start = start.to_s.strip
22
58
  @start = Util.parsedate(start)
23
59
  raise "invalid start date (#{start})" unless @start
24
60
  end
25
61
 
26
- # Number of rounds. Is either unknown (nil) or a positive integer.
62
+ # Set the number of rounds. Is either unknown (_nil_) or a positive integer.
27
63
  def rounds=(rounds)
28
64
  @rounds = case rounds
29
65
  when nil then nil
@@ -34,7 +70,7 @@ module ICU
34
70
  raise "invalid number of rounds (#{rounds})" unless @rounds.nil? || @rounds > 0
35
71
  end
36
72
 
37
- # Web site. Either unknown or a reasonably valid looking URL.
73
+ # Set the tournament web site. Should be either unknown (_nil_) or a reasonably valid looking URL.
38
74
  def site=(site)
39
75
  @site = site.to_s.strip
40
76
  @site = nil if @site == ''
@@ -42,7 +78,7 @@ module ICU
42
78
  raise "invalid site (#{site})" unless @site.nil? || @site.match(/^https?:\/\/[-\w]+(\.[-\w]+)+(\/[^\s]*)?$/i)
43
79
  end
44
80
 
45
- # Add players.
81
+ # Add a new player to the tournament. Must have a unique player number.
46
82
  def add_player(player)
47
83
  raise "invalid player" unless player.class == ICU::Player
48
84
  raise "player number (#{player.num}) should be unique" if @player[player.num]
@@ -54,17 +90,22 @@ module ICU
54
90
  @player[num]
55
91
  end
56
92
 
57
- # Return an array of all the players.
93
+ # Return an array of all players in order of their player numbers.
58
94
  def players
59
- @player.values
95
+ @player.values.sort_by{ |p| p.num }
60
96
  end
61
97
 
62
- # Lookup a player in the tournament.
98
+ # Lookup a player in the tournament by player number, returning _nil_ if the player number does not exist.
63
99
  def find_player(player)
64
100
  players.find { |p| p == player }
65
101
  end
66
102
 
67
- # Add results.
103
+ # Add a result to a tournament. An exception is raised if the players referenced in the result (by number)
104
+ # do not exist in the tournament. The result, which remember is from the perspective of one of the players,
105
+ # is added to that player's results. Additionally, the reverse of the result is automatically added to the player's
106
+ # opponent, unless the opponent does not exist (e.g. byes, walkovers). By default, if the result is rateable
107
+ # then the opponent's result will also be rateable. To make the opponent's result unrateable, set the optional
108
+ # second parameter to false.
68
109
  def add_result(result, reverse_rateable=true)
69
110
  raise "invalid result" unless result.class == ICU::Result
70
111
  raise "result round number (#{result.round}) inconsistent with number of tournament rounds" if @rounds && result.round > @rounds
@@ -2,10 +2,85 @@ require 'fastercsv'
2
2
 
3
3
  module ICU
4
4
  class Tournament
5
+
6
+ =begin rdoc
7
+
8
+ == Foreign CSV
9
+
10
+ This is a format ({specification}[http://www.icu.ie/articles/display.php?id=172]) used by the ICU[http://icu.ie]
11
+ for players to submit their individual results in foreign tournaments for domestic rating.
12
+
13
+ Suppose, for example, that the following data is the file <em>tournament.csv</em>:
14
+
15
+ Event,"Isle of Man Masters, 2007"
16
+ Start,2007-09-22
17
+ Rounds,9
18
+ Website,http://www.bcmchess.co.uk/monarch2007/
19
+
20
+ Player,456,Fox,Anthony
21
+ 1,0,B,Taylor,Peter P.,2209,,ENG
22
+ 2,=,W,Nadav,Egozi,2205,,ISR
23
+ 3,=,B,Cafolla,Peter,2048,,IRL
24
+ 4,1,W,Spanton,Tim R.,1982,,ENG
25
+ 5,1,B,Grant,Alan,2223,,SCO
26
+ 6,0,-
27
+ 7,=,W,Walton,Alan J.,2223,,ENG
28
+ 8,0,B,Bannink,Bernard,2271,FM,NED
29
+ 9,=,W,Phillips,Roy,2271,,MAU
30
+ Total,4
31
+
32
+ This file can be parsed as follows.
33
+
34
+ data = open('tournament.csv') { |f| f.read }
35
+ parser = ICU::Tournament::ForeignCSV.new
36
+ tournament = parser.parse(data)
37
+
38
+ If the file is correctly specified, the return value from the <em>parse</em> method is an instance of
39
+ ICU::Tournament (rather than <em>nil</em>, which indicates an error). In this example the file is valid, so:
40
+
41
+ tournament.name # => "Isle of Man Masters, 2007"
42
+ tournament.start # => "2007-09-22"
43
+ tournament.rounds # => 9
44
+ tournament.website # => "http://www.bcmchess.co.uk/monarch2007/"
45
+
46
+ The main player (the player whose results are being reported for rating) played 9 rounds
47
+ but only 8 other players (he had a bye in round 6), so the total number of players is 9.
48
+
49
+ tournament.players.size # => 9
50
+
51
+ Each player has a unique number for the tournament. The main player always occurs first in this type of file, so his number is 1.
52
+
53
+ player = tournament.player(1)
54
+ player.name # => "Fox, Anthony"
55
+
56
+ This player has 4 points from 9 rounds but only 8 of his results are are rateable (because of the bye).
57
+
58
+ player.points # => 4.0
59
+ player.results.size # => 9
60
+ player.results.find_all{ |r| r.rateable }.size # => 8
61
+
62
+ The other players all have numbers greater than 1.
63
+
64
+ opponents = tournamnet.players.reject { |o| o.num == 1 }
65
+
66
+ There are 8 opponents of the main player, each with exactly one game.
67
+
68
+ opponents.size # => 8
69
+ opponents.find_all{ |o| o.results.size == 1}.size # => 8
70
+
71
+ However, none of the opponents' results are rateable. For example:
72
+
73
+ opponent = tournament.players(2)
74
+ opponent.name # => "Taylor, Peter P."
75
+ opponent.results[0].rateable # => false
76
+
77
+ =end
78
+
5
79
  class ForeignCSV
6
80
  attr_reader :error
7
81
 
8
- # Parse CSV data returning a Tournament on success or a nil on failure (and a message retrievable via the error method).
82
+ # Parse CSV data returning a Tournament on success or a nil on failure.
83
+ # In the case of failure, an error message can be retrived via the <em>error</em> method.
9
84
  def parse(csv)
10
85
  begin
11
86
  parse!(csv)
@@ -62,6 +62,60 @@ CSV
62
62
  check_player(4, 'Linda', 'Powell', 1, 0, 0.0, :rating => 1850, :fed => 'WLS')
63
63
  end
64
64
  end
65
+
66
+ context "the rdoc example tournament" do
67
+ before(:all) do
68
+ @csv = <<CSV
69
+ Event,"Isle of Man Masters, 2007"
70
+ Start,2007-09-22
71
+ Rounds,9
72
+ Website,http://www.bcmchess.co.uk/monarch2007/
73
+
74
+ Player,456,Fox,Anthony
75
+ 1,0,B,Taylor,Peter P.,2209,,ENG
76
+ 2,=,W,Nadav,Egozi,2205,,ISR
77
+ 3,=,B,Cafolla,Peter,2048,,IRL
78
+ 4,1,W,Spanton,Tim R.,1982,,ENG
79
+ 5,1,B,Grant,Alan,2223,,SCO
80
+ 6,0,-
81
+ 7,=,W,Walton,Alan J.,2223,,ENG
82
+ 8,0,B,Bannink,Bernard,2271,FM,NED
83
+ 9,=,W,Phillips,Roy,2271,,MAU
84
+ Total,4
85
+ CSV
86
+ @f = ForeignCSV.new
87
+ @t = @f.parse!(@csv)
88
+ @p = @t.player(1)
89
+ @o = @t.players.reject { |o| o.num == 1 }
90
+ @r = @t.player(2)
91
+ end
92
+
93
+ it "should have correct basic details" do
94
+ @t.name.should == 'Isle of Man Masters, 2007'
95
+ @t.start.should == '2007-09-22'
96
+ @t.rounds.should == 9
97
+ @t.site.should == 'http://www.bcmchess.co.uk/monarch2007/'
98
+ end
99
+
100
+ it "should have the right number of players in the right order" do
101
+ @t.players.size.should == 9
102
+ @t.players.inject(''){ |a,o| a << o.num.to_s }.should == '123456789'
103
+ end
104
+
105
+ it "should have the right details for the main player" do
106
+ @p.name.should == "Fox, Anthony"
107
+ @p.results.size == 9
108
+ @p.results.find_all{ |r| r.rateable }.size.should == 8
109
+ @p.points.should == 4.0
110
+ end
111
+
112
+ it "should have the right details for the opponents" do
113
+ @o.size.should == 8
114
+ @o.find_all{ |o| o.results.size == 1}.size.should == 8
115
+ @r.name.should == "Taylor, Peter P."
116
+ @r.results[0].rateable.should be_false
117
+ end
118
+ end
65
119
 
66
120
  context "a tournament with more than one player" do
67
121
  before(:all) do
@@ -7,7 +7,7 @@ module ICU
7
7
  lambda do
8
8
  t = Tournament.new('Bangor Bash', '2009-11-09')
9
9
  t.add_player(Player.new('Bobby', 'Fischer', 1))
10
- t.add_player(Player.new('Garry', 'Gary Kasparov', 2))
10
+ t.add_player(Player.new('Garry', 'Kasparov', 2))
11
11
  t.add_player(Player.new('Mark', 'Orr', 3))
12
12
  t.add_result(Result.new(1, 1, '=', :opponent => 2, :colour => 'W'))
13
13
  t.add_result(Result.new(2, 2, 'L', :opponent => 3, :colour => 'W'))
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sanichi-chess_icu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Orr
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-12 00:00:00 -07:00
12
+ date: 2009-04-18 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15