sanichi-chess_icu 0.2.1 → 0.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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