Class: ICU::Tournament::SwissPerfect
- Inherits:
-
Object
- Object
- ICU::Tournament::SwissPerfect
- Defined in:
- lib/icu_tournament/tournament_sp.rb
Overview
SwissPerfect
This is the format produced by the Windows program, SwissPerfect. It consists of three files with the same name but different endings: .ini for meta data such as tournament name and tie-break rules, .trn for the player details such as name and rating, and .sco for the results. The first file is text and the other two are in an old binary format known as DBase 3.
To parse such a set of files, use either the parse_file! or parse_file method supplying the name of any one of the three files or just the stem name without any ending. In case of error, such as any of the files not being found, parse_file! will throw an exception while parse_file will return nil and record an error message. As well as a file name or stem name, you should also supply a start date in the options as SwissPerfect does not record this information.
parser = ICU::Tournament::SwissPerfect.new
tournament = parser.parse_file('champs', :start => '2010-07-03') # looks for "champs.ini", "champs.trn" and "champs.sco"
puts tournament ? 'ok' : "problem: #{parser.error}"
Alternatively, if all three files are in a ZIP archive, the parser will extract them if the name of the archive file is supplied to the parse_file method and it ends in “.zip” (case insensitive):
tournament = parser.parse_file('champs.zip', :start => '2010-07-03')
Or, if the file is a ZIP archive but it’s name doesn’t end in “.zip”, that can be signalled with an option:
tournament = parser.parse_file('/tmp/a84f21ge', :zip => true, :start => '2010-07-03')
Note there must be only three files in the archive, they must all have the same stem name and their endings should be “.ini”, “.trn” and “.sco” (case insensitive).
If no start date is supplied it will default to 2000-01-01, and can be reset later.
tournament = parser.parse_file('champs.zip')
tournament.start # => '2000-01-01'
tournament.start = '2010-07-03'
By default, the parser extracts local ratings and IDs from the SwissPerfect files. If international ratings or IDs are required instead, use the options id and rating. For example:
tournament = parser.parse_file('ncc', :start => '2010-05-08')
tournament.player(2).id # => 12379 (ICU ID)
tournament.player(2). # => 2556 (ICU rating)
tournament = parser.parse_file('ncc', :start => '2010-05-08', :id => 'intl', :rating => 'intl')
tournament.player(2).id # => 1205064 (FIDE ID)
tournament.player(2). # => 2530 (FIDE rating)
By default, the parse will fail completely if the “.trn” file contains any invalid federations (see ICU::Federation). There are two alternative behaviours controlled by setting the fed option:
tournament = parser.parse_file('ncc', :start => '2010-05-08', :fed == 'skip') # => silently skips invalid federations
tournament = parser.parse_file('ncc', :start => '2010-05-08', :fed == 'ignore') # => ignores all federations
Note that federations that don’t match 3 letters are always silently skipped.
Because the data is in three parts, some of which are in a legacy binary format, serialization to this format is not supported. Instead, a method is provided to serialize any tournament type into the text export format of erfect, an example of which is shown below.
No Name Loc Id Total 1 2 3
1 Griffiths, Ryan-Rhys 6897 3 4:W 2:W 3:W
2 Flynn, Jamie 5226 2 3:W 1:L 4:W
3 Hulleman, Leon 6409 1 2:L 4:W 1:L
4 Dunne, Thomas 10914 0 1:L 3:L 2:L
This format is important in Irish chess, as it’s the format used to submit results to the MicroSoft Access implementation of the ICU ratings database.
swiss_perfect = tournament.serialize('SwissPerfect')
The order of players in the serialized output is always by player number and as a side effect of serialization, the player numbers will be adjusted to ensure they range from 1 to the total number of players (i.e. renumbered in order). If you would prefer rank-order instead, then you must first renumber the players by rank (the default renumbering method) before serializing. For example:
swiss_perfect = tournament.renumber.serialize('SwissPerfect')
There should be no need to explicitly rank the tournament first, as that information is already present in SwissPerfect files (i.e. each player should already have a rank after the files have been parsed). Additionally, the tie break rules used for the tournament are available from the tie_break method, for example:
tournament.tie_breaks # => [:buchholz, :harkness]
Should you wish to rank the tournament using a different set of tie-break rules, you can do something like the following:
tournament.tie_breaks = [:wins, :blacks]
swiss_perfect = tournament.rerank.renumber.serialize('SwissPerfect')
See ICU::Tournament for more about tie-breaks.
Constant Summary collapse
- TRN =
{ :dob => "BIRTH_DATE", :fed => "FEDER", :first_name => "FIRSTNAME", :gender => "SEX", :id => ["LOC_ID", "INTL_ID"], :last_name => "SURNAME", :num => "ID", :rank => "ORDER", :rating => ["LOC_RTG", "INTL_RTG"], }
- SCO =
not used: ABSENT BOARD CLUB FORB_PAIRS LATE_ENTRY LOC_RTG2 MEMO TEAM TECH_SCORE WITHDRAWAL (START_NO, BONUS used below)
%w{ROUND WHITE BLACK W_SCORE B_SCORE W_TYPE B_TYPE}
Instance Attribute Summary collapse
-
#error ⇒ Object
readonly
Returns the value of attribute error.
Instance Method Summary collapse
-
#parse_file(file, arg = {}) ⇒ Object
Parse SP data returning an ICU::Tournament or a nil on failure.
-
#parse_file!(file, arg = {}) ⇒ Object
Parse SP data returning a Tournament or raising an exception on error.
-
#serialize(t) ⇒ Object
Serialise a tournament to SwissPerfect text export format.
Instance Attribute Details
#error ⇒ Object (readonly)
Returns the value of attribute error.
105 106 107 |
# File 'lib/icu_tournament/tournament_sp.rb', line 105 def error @error end |
Instance Method Details
#parse_file(file, arg = {}) ⇒ Object
Parse SP data returning an ICU::Tournament or a nil on failure. In the latter case, an error message will be available via the error method.
138 139 140 141 142 143 144 145 |
# File 'lib/icu_tournament/tournament_sp.rb', line 138 def parse_file(file, arg={}) begin parse_file!(file, arg) rescue => ex @error = ex. nil end end |
#parse_file!(file, arg = {}) ⇒ Object
Parse SP data returning a Tournament or raising an exception on error.
122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/icu_tournament/tournament_sp.rb', line 122 def parse_file!(file, arg={}) @t = Tournament.new('Dummy', '2000-01-01') @t.start = arg[:start] if arg[:start] @bonus = {} @start_no = {} ini, trn, sco = get_files(file, arg) parse_ini(ini) parse_trn(trn, arg) parse_sco(sco) fixup @t.validate!(:rerank => true) @t end |
#serialize(t) ⇒ Object
Serialise a tournament to SwissPerfect text export format.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/icu_tournament/tournament_sp.rb', line 148 def serialize(t) return nil unless t.class == ICU::Tournament && t.players.size > 2; # Ensure a nice set of numbers. t.renumber(:order) # Widths for the rank, name and ID and the number of rounds. m1 = t.players.inject(2) { |l, p| p.num.to_s.length > l ? p.num.to_s.length : l } m2 = t.players.inject(4) { |l, p| p.name.length > l ? p.name.length : l } m3 = t.players.inject(6) { |l, p| p.id.to_s.length > l ? p.id.to_s.length : l } rounds = t.last_round # The header, followed by a blank line. formats = ["%-#{m1}s", "%-#{m2}s", "%-#{m3}s", "%-5s"] (1..rounds).each { |r| formats << "%#{m1}d " % r } sp = formats.join("\t") % ['No', 'Name', 'Loc Id', 'Total'] sp << "\r\n\r\n" # Adjust the round parts of the formats for players results. (1..t.last_round).each { |r| formats[r+3] = "%#{m1+2}s" } # Now add a line for each player. t.players.each { |p| sp << p.to_sp_text(rounds, "#{formats.join(%Q{\t})}\r\n") } # And return the whole lot. sp end |