"Exercise: Tournament"

tournament: rowCollection
"Tally the results of a small football competition. Based on an input containing which team played against which and what the outcome was, return the table rows with the summary.
    What do those abbreviations mean?
    - MP: Matches Played
    - W: Matches Won
    - D: Matches Drawn (Tied)
    - L: Matches Lost
    - P: Points
    A win earns a team 3 points. A draw earns 1. A loss earns 0.
    The outcome is ordered by points, descending. In case of a tie, teams are ordered alphabetically."
"(self new tournament: #('Allegoric Alaskans;Blithering Badgers;win' 'Devastating Donkeys;Courageous Californians;draw' 'Devastating Donkeys;Allegoric Alaskans;win' 'Courageous Californians;Blithering Badgers;loss' 'Blithering Badgers;Devastating Donkeys;loss' 'Allegoric Alaskans;Courageous Californians;win')) >>> #('Team                           | MP |  W |  D |  L |  P' 'Devastating Donkeys            |  3 |  2 |  1 |  0 |  7' 'Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6' 'Blithering Badgers             |  3 |  1 |  0 |  2 |  3' 'Courageous Californians        |  3 |  0 |  1 |  2 |  1')"
| results outcomeInverse tallies |
    results := Dictionary new.
    outcomeInverse := Dictionary newFrom: { 'win'->'loss'. 'loss'->'win'. 'draw'->'draw' }.

    rowCollection do: [:line |
        | parts teamA teamB outcome inverse tdata |
        parts := $; split: line.
        teamA := parts first.
        teamB := parts second.
        outcome := parts third.
        tdata := results at: teamA ifAbsentPut: [ Dictionary new ].
        tdata at: outcome put: ((tdata at: outcome ifAbsent: [ 0 ]) + 1).
        inverse := outcomeInverse at: outcome.
        tdata := results at: teamB ifAbsentPut: [ Dictionary new ].
        tdata at: inverse put: ((tdata at: inverse ifAbsent: [ 0 ]) + 1).
    ].

    tallies := results associations asSortedCollection: [:a :b |
        | wA dA pA wB dB pB |
        wA := (a value at: 'win' ifAbsent: [ 0 ]).
        dA := (a value at: 'draw' ifAbsent: [ 0 ]).
        pA := wA * 3 + dA.
        wB := (b value at: 'win' ifAbsent: [ 0 ]).
        dB := (b value at: 'draw' ifAbsent: [ 0 ]).
        pB := wB * 3 + dB.
        (pA > pB) or: [ (pA = pB) and: [ a key <= b key ] ]
    ].

    ^ (String streamContents: [:out |
        out nextPutAll: 'Team                           | MP |  W |  D |  L |  P'; cr.
        tallies do: [:assoc |
            | name wins draws losses mp points |
            name := assoc key.
            wins := (assoc value at: 'win' ifAbsent: [ 0 ]).
            draws := (assoc value at: 'draw' ifAbsent: [ 0 ]).
            losses := (assoc value at: 'loss' ifAbsent: [ 0 ]).
            mp := wins + draws + losses.
            points := wins * 3 + draws.
            out nextPutAll: (name padRightTo: 31);
                nextPutAll: ('|  {1} |  {2} |  {3} |  {4} |  {5}'
                    format: { mp. wins. draws. losses. points });
                cr
        ]
    ]) lines


"Tests"
testTournament
| header |
  header := 'Team                           | MP |  W |  D |  L |  P'.

  self assert: (task tournament: #()) equals: #( 'Team                           | MP |  W |  D |  L |  P' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3'
    'Blithering Badgers             |  1 |  0 |  0 |  1 |  0' ).

  self assert: (task tournament: #('Blithering Badgers;Allegoric Alaskans;loss')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  1 |  1 |  0 |  0 |  3'
    'Blithering Badgers             |  1 |  0 |  0 |  1 |  0' ).

  self assert: (task tournament: #('Blithering Badgers;Allegoric Alaskans;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Blithering Badgers             |  1 |  1 |  0 |  0 |  3'
    'Allegoric Alaskans             |  1 |  0 |  0 |  1 |  0' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;draw')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  1 |  0 |  1 |  0 |  1'
    'Blithering Badgers             |  1 |  0 |  1 |  0 |  1' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;win' 'Allegoric Alaskans;Blithering Badgers;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6'
    'Blithering Badgers             |  2 |  0 |  0 |  2 |  0' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;loss' 'Allegoric Alaskans;Blithering Badgers;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  2 |  1 |  0 |  1 |  3'
    'Blithering Badgers             |  2 |  1 |  0 |  1 |  3' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;win' 'Blithering Badgers;Courageous Californians;win' 'Courageous Californians;Allegoric Alaskans;loss')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  2 |  2 |  0 |  0 |  6'
    'Blithering Badgers             |  2 |  1 |  0 |  1 |  3'
    'Courageous Californians        |  2 |  0 |  0 |  2 |  0' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;win' 'Devastating Donkeys;Courageous Californians;draw' 'Devastating Donkeys;Allegoric Alaskans;win' 'Courageous Californians;Blithering Badgers;loss' 'Blithering Badgers;Devastating Donkeys;loss' 'Allegoric Alaskans;Courageous Californians;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Devastating Donkeys            |  3 |  2 |  1 |  0 |  7'
    'Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6'
    'Blithering Badgers             |  3 |  1 |  0 |  2 |  3'
    'Courageous Californians        |  3 |  0 |  1 |  2 |  1' ).

  self assert: (task tournament: #('Allegoric Alaskans;Blithering Badgers;loss' 'Devastating Donkeys;Allegoric Alaskans;loss' 'Courageous Californians;Blithering Badgers;draw' 'Allegoric Alaskans;Courageous Californians;win')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  3 |  2 |  0 |  1 |  6'
    'Blithering Badgers             |  2 |  1 |  1 |  0 |  4'
    'Courageous Californians        |  2 |  0 |  1 |  1 |  1'
    'Devastating Donkeys            |  1 |  0 |  0 |  1 |  0' ).

  self assert: (task tournament: #('Courageous Californians;Devastating Donkeys;win' 'Allegoric Alaskans;Blithering Badgers;win' 'Devastating Donkeys;Allegoric Alaskans;loss' 'Courageous Californians;Blithering Badgers;win' 'Blithering Badgers;Devastating Donkeys;draw' 'Allegoric Alaskans;Courageous Californians;draw')) equals: #( 'Team                           | MP |  W |  D |  L |  P'
    'Allegoric Alaskans             |  3 |  2 |  1 |  0 |  7'
    'Courageous Californians        |  3 |  2 |  1 |  0 |  7'
    'Blithering Badgers             |  3 |  0 |  1 |  2 |  1'
    'Devastating Donkeys            |  3 |  0 |  1 |  2 |  1' )