Class: ICU::Tournament::SwissPerfect

Inherits:
Object
  • Object
show all
Defined in:
lib/icu_tournament/tournament_sp.rb

Overview

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'

SwissPerfect files have slots for both local and international IDs and ratings and these, if present (and if integers) are copied to the id, fide, rating and fide_rating attributes.

tournament = parser.parse_file('ncc', :start => '2010-05-08')
tournament.player(2).id          # =>  12379 (ICU ID)
tournament.player(2).fide_id     # =>  1205064 (FIDE ID)
tournament.player(2).rating      # =>  2556 (ICU rating)
tournament.player(2).fide_rating # =>  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 SwissPerfect (see ICU::Tournament::SPExport).

Constant Summary collapse

TRN =
{
  :dob         => "BIRTH_DATE",
  :fed         => "FEDER",
  :first_name  => "FIRSTNAME",
  :gender      => "SEX",
  :id          => "LOC_ID",
  :fide_id     => "INTL_ID",
  :last_name   => "SURNAME",
  :num         => "ID",
  :rank        => "ORDER",
  :rating      => "LOC_RTG",
  :fide_rating => "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

Instance Method Summary collapse

Instance Attribute Details

#errorObject (readonly)

Returns the value of attribute error.



63
64
65
# File 'lib/icu_tournament/tournament_sp.rb', line 63

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.



98
99
100
101
102
103
104
105
# File 'lib/icu_tournament/tournament_sp.rb', line 98

def parse_file(file, arg={})
  begin
    parse_file!(file, arg)
  rescue => ex
    @error = ex.message
    nil
  end
end

#parse_file!(file, arg = {}) ⇒ Object

Parse SP data returning a Tournament or raising an exception on error.



82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/icu_tournament/tournament_sp.rb', line 82

def parse_file!(file, arg={})
  @t = Tournament.new('Unspecified', '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, arg = {}) ⇒ Object

Serialise a tournament to SwissPerfect text export format.



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/icu_tournament/tournament_sp.rb', line 108

def serialize(t, arg={})
  t.validate!(:type => self)

  # 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

#validate!(t) ⇒ Object

Additional tournament validation rules for this specific type.



137
138
139
# File 'lib/icu_tournament/tournament_sp.rb', line 137

def validate!(t)
  # None.
end