Skip to content

Commit

Permalink
Assign some players to fixed tables (#341)
Browse files Browse the repository at this point in the history
* Refer to games rather than tables for double elim

* Add fixed table number column for players

* Apply fixed table number

* Replace each_with_index with each

* Call PairingSorters::Ranked directly

* Add frozen_string_literal to DB migration

* Fix RoundsController spec

* Extract SwissTables module

* Add swiss_tables_spec.rb

* Remove extra empty line

* Test choosing lowest fixed number

* Test excluding fixed table numbers for other tables

* Remove extra whitespace

* Swap fixed table numbers so they need to be reversed

* Input fixed table number

* Adjust table number input

* Adjust manual-seed class name

* Adjust colour of unassign table number link

* Assign table number when adding player

* Test bye before fixed tables

* Test bye after fixed tables

* Change icon for fixed table number
  • Loading branch information
haradan authored Sep 19, 2024
1 parent 2586f97 commit fddbbd5
Show file tree
Hide file tree
Showing 20 changed files with 297 additions and 34 deletions.
18 changes: 18 additions & 0 deletions app/assets/javascripts/table_number.js.coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
$(document).on 'turbolinks:load', ->
window.assignTableNumber = (playerId) ->
if playerId
form = $("form#edit_player_#{playerId}")
else
form = $('form.register-player')
form.find('.form-group.table-number').removeClass('d-none')
form.find('.assign-table-number').addClass('d-none')
form.find('.unassign-table-number').removeClass('d-none')
window.unassignTableNumber = (playerId) ->
if playerId
form = $("form#edit_player_#{playerId}")
else
form = $('form.register-player')
form.find('.form-group.table-number input').val(null)
form.find('.form-group.table-number').addClass('d-none')
form.find('.assign-table-number').removeClass('d-none')
form.find('.unassign-table-number').addClass('d-none')
4 changes: 3 additions & 1 deletion app/assets/stylesheets/players.sass
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.players
.manual_seed
.manual-seed
width: 80px
input.table-number
width: 170px

.identities_form
.form-group, .input
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/players_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ def view_decks
def player_params
params.require(:player)
.permit(:name, :pronouns, :corp_identity, :runner_identity, :corp_deck, :runner_deck,
:first_round_bye, :manual_seed, :include_in_stream)
:first_round_bye, :manual_seed, :include_in_stream, :fixed_table_number)
end

def organiser_view?
Expand Down
1 change: 1 addition & 0 deletions app/controllers/rounds_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ def pairings_data
{
id: pairing.id,
table_number: pairing.table_number,
table_label: pairing.table_label,
policy: {
view_decks:
},
Expand Down
10 changes: 5 additions & 5 deletions app/frontend/pairings/Pairing.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
export let stage: Stage;
export let round: Round;
export let pairing: Pairing;
let left_player = pairing.player1
let right_player = pairing.player2
let left_player = pairing.player1;
let right_player = pairing.player2;
if (stage.format == 'single_sided_swiss' && pairing.player2.side == 'corp') {
left_player = pairing.player2
right_player = pairing.player1
left_player = pairing.player2;
right_player = pairing.player1;
}
</script>

<div class="row m-1 round_pairing align-items-center table_{pairing.table_number}">
<div class="col-sm-2">
Table {pairing.table_number}
{pairing.table_label}
</div>
{#if pairing.policy.view_decks}
<a href="{round.id}/pairings/{pairing.id}/view_decks">
Expand Down
1 change: 1 addition & 0 deletions app/frontend/pairings/PairingsData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export type Round = {
export type Pairing = {
id: number;
table_number: number;
table_label: string;
policy: PairingPolicies;
player1: Player;
player2: Player;
Expand Down
8 changes: 8 additions & 0 deletions app/models/nil_player.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,12 @@ def runner_identity_object
def include_in_stream?
true
end

def fixed_table_number
nil
end

def fixed_table_number?
false
end
end
16 changes: 16 additions & 0 deletions app/models/pairing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,22 @@ def side_for(player)
player1 == player ? player1_side : player2_side
end

def table_label
stage.double_elim? ? "Game #{table_number}" : "Table #{table_number}"
end

def fixed_table_number?
players.any?(&:fixed_table_number?)
end

def fixed_table_number
players.filter(&:fixed_table_number?).map(&:fixed_table_number).min
end

def bye?
!(player1_id? && player2_id?)
end

private

def normalise_scores_before_save
Expand Down
13 changes: 1 addition & 12 deletions app/services/pairing_strategies/swiss.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def pair!
round.pairings.create(pairing_params(pairing))
end

apply_numbers!(PairingSorters::Ranked)
SwissTables.assign_table_numbers!(round.pairings)
end

def self.get_pairings(players)
Expand Down Expand Up @@ -68,17 +68,6 @@ def auto_score(pairing, player_index)
pairing[player_index] == SwissImplementation::Bye ? @bye_loser_score : @bye_winner_score
end

def apply_numbers!(sorter)
sorter.sort(round.pairings.non_bye).each_with_index do |pairing, i|
pairing.update(table_number: i + 1)
end

non_bye_tables = round.pairings.non_bye.count
round.pairings.bye.each_with_index do |pairing, i|
pairing.update(table_number: i + non_bye_tables + 1)
end
end

def players_with_byes
return players.with_first_round_bye if first_round?

Expand Down
57 changes: 57 additions & 0 deletions app/services/swiss_tables.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# frozen_string_literal: true

module SwissTables
def self.assign_table_numbers!(pairings)
PairingOrder.new(pairings).apply_numbers!
end

class PairingOrder
def initialize(pairings)
@non_byes = []
@byes = []
@fixed_tables = []
pairings.each do |pairing|
if pairing.fixed_table_number?
@fixed_tables << pairing
elsif pairing.bye?
@byes << pairing
else
@non_byes << pairing
end
end
end

def apply_numbers!
numbers = Numbers.new
@fixed_tables.each do |pairing|
number = pairing.fixed_table_number
numbers.exclude_fixed number
pairing.update(table_number: number)
end
PairingSorters::Ranked.sort(@non_byes).each do |pairing|
pairing.update(table_number: numbers.next)
end
@byes.each do |pairing|
pairing.update(table_number: numbers.next)
end
end
end

class Numbers
def initialize
@fixed_numbers = Set[]
@next_number = 1
end

def exclude_fixed(number)
@fixed_numbers << number
end

def next
@next_number += 1 while @fixed_numbers.include? @next_number
number = @next_number
@next_number += 1
number
end
end
end
8 changes: 6 additions & 2 deletions app/views/players/_form.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@
input_html: { class: 'mr-1' },
wrapper_html: { class: 'align-middle mr-3' }
- if @tournament.manual_seed
= f.input :manual_seed, placeholder: 'Set seed', inline_label: 'Manual Seed', required: false, as: :string,
= f.input :manual_seed, placeholder: 'Set seed', label: 'Manual Seed', required: false, as: :string,
wrapper_html: { class: 'form-group my-2' },
input_html: { class: 'form-control manual_seed' },
input_html: { class: 'form-control manual-seed' },
label_html: { class: 'align-middle d-inline-block mr-1' }
= f.input :fixed_table_number, placeholder: 'Enter table number', label: 'Table Number', required: false, as: :string,
wrapper_html: { class: "form-group my-2 table-number #{defined?(player) && player.fixed_table_number? ? '' : 'd-none'}" },
input_html: { class: 'form-control table-number' },
label_html: { class: 'align-middle d-inline-block mr-1' }
14 changes: 13 additions & 1 deletion app/views/players/index.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@

.alert.alert-secondary.mt-4
h5.mb-3 Register New Player
= simple_form_for :player, url: tournament_players_path(@tournament), html: { class: 'form-inline d-block' } do |f|
= simple_form_for :player, url: tournament_players_path(@tournament), html: { class: 'form-inline d-block register-player' } do |f|
= render 'form', f: f, organiser_view: true, new_registration: true
.mt-2.text-right
a.btn.btn-link.text-info.mr-2.assign-table-number onclick="assignTableNumber()"
=> fa_icon 'bookmark'
| Assign table number
a.btn.btn-link.text-warning.mr-2.unassign-table-number.d-none onclick="unassignTableNumber()"
=> fa_icon 'bookmark'
| Unassign table number
= button_tag type: :submit, class: 'btn btn-success',
data: { confirm: @tournament.registration_closed? ? 'Tournament is closed, add new player anyway?' : nil } do
=> fa_icon 'plus'
Expand Down Expand Up @@ -104,6 +110,12 @@
=< link_to registration_tournament_player_path(@tournament, player), class: 'btn btn-link text-info mr-2' do
=> fa_icon 'eye'
| View decks
a.btn.btn-link.text-info.mr-2.assign-table-number onclick="assignTableNumber(#{player.id})" class="#{player.fixed_table_number? ? 'd-none' : ''}"
=> fa_icon 'bookmark'
| Assign table number
a.btn.btn-link.text-warning.mr-2.unassign-table-number onclick="unassignTableNumber(#{player.id})" class="#{player.fixed_table_number? ? '' : 'd-none'}"
=> fa_icon 'bookmark'
| Unassign table number
= button_tag type: :submit, class: 'btn btn-link text-success mr-2' do
=> fa_icon 'floppy-o'
| Save
Expand Down
2 changes: 1 addition & 1 deletion app/views/rounds/_pairings.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
- right_player_number = 1
.row.m-1.round_pairing.align-items-center class="table_#{pairing.table_number} #{'reported' if pairing.reported? && current_user == @tournament.user}"
.col-sm-2
| Table #{pairing.table_number}
| #{pairing.table_label}
- if @tournament.allow_streaming_opt_out? && policy(round.tournament).edit?
- if pairing.player_ids.all? { |player_id| @players[player_id].include_in_stream? }
= fa_icon 'video-camera',
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20240909235836_add_fixed_table_to_players.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class AddFixedTableToPlayers < ActiveRecord::Migration[7.1]
def change
add_column :players, :fixed_table_number, :integer
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.1].define(version: 2024_07_26_082848) do
ActiveRecord::Schema[7.1].define(version: 2024_09_09_235836) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -137,6 +137,7 @@
t.boolean "include_in_stream", default: true
t.bigint "corp_identity_ref_id"
t.bigint "runner_identity_ref_id"
t.integer "fixed_table_number"
t.index ["corp_identity_ref_id"], name: "index_players_on_corp_identity_ref_id"
t.index ["runner_identity_ref_id"], name: "index_players_on_runner_identity_ref_id"
t.index ["tournament_id"], name: "index_players_on_tournament_id"
Expand Down
5 changes: 5 additions & 0 deletions spec/feature/players/update_players_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
fill_in :player_runner_identity, with: 'Gabriel Santiago'
check :player_first_round_bye
fill_in :player_manual_seed, with: 3
fill_in :player_fixed_table_number, with: 20
click_button 'Save'
end
end
Expand All @@ -38,6 +39,10 @@
expect(tournament.players.first.manual_seed).to eq(3)
end

it 'can update fixed table' do
expect(tournament.players.first.fixed_table_number).to eq(20)
end

it 'does not update user id' do
expect(tournament.players.first.user_id).to be_nil
end
Expand Down
21 changes: 14 additions & 7 deletions spec/requests/rounds_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@
'player1' => player_with_no_ids('Charlie (she/her)'),
'player2' => player_with_no_ids('Bob (he/him)'),
'policy' => { 'view_decks' => false },
'score_label' => ' - ', 'table_number' => 1, 'two_for_one' => false },
'score_label' => ' - ', 'two_for_one' => false,
'table_label' => 'Table 1', 'table_number' => 1 },
{ 'intentional_draw' => false,
'player1' => player_with_no_ids('Alice (she/her)'),
'player2' => bye_player,
'policy' => { 'view_decks' => false },
'score_label' => '6 - 0', 'table_number' => 2, 'two_for_one' => false }
'score_label' => '6 - 0', 'two_for_one' => false,
'table_label' => 'Table 2', 'table_number' => 2 }
], 'pairings_reported' => 1
}
] }]
Expand All @@ -93,12 +95,14 @@
'player1' => player_with_no_ids('Charlie (she/her)'),
'player2' => player_with_no_ids('Bob (he/him)'),
'policy' => { 'view_decks' => false }, # sees player view as a player
'score_label' => ' - ', 'table_number' => 1, 'two_for_one' => false },
'score_label' => ' - ', 'two_for_one' => false,
'table_label' => 'Table 1', 'table_number' => 1 },
{ 'intentional_draw' => false,
'player1' => player_with_no_ids('Alice (she/her)'),
'player2' => bye_player,
'policy' => { 'view_decks' => false }, # sees player view as a player
'score_label' => '6 - 0', 'table_number' => 2, 'two_for_one' => false }
'score_label' => '6 - 0', 'two_for_one' => false,
'table_label' => 'Table 2', 'table_number' => 2 }
], 'pairings_reported' => 1
}
] }]
Expand Down Expand Up @@ -129,12 +133,14 @@
'player1' => player_with_no_ids('Charlie (she/her)'),
'player2' => player_with_no_ids('Bob (he/him)'),
'policy' => { 'view_decks' => false },
'score_label' => ' - ', 'table_number' => 1, 'two_for_one' => false },
'score_label' => ' - ', 'two_for_one' => false,
'table_label' => 'Table 1', 'table_number' => 1 },
{ 'intentional_draw' => false,
'player1' => player_with_no_ids('Alice (she/her)'),
'player2' => bye_player,
'policy' => { 'view_decks' => false },
'score_label' => '6 - 0', 'table_number' => 2, 'two_for_one' => false }
'score_label' => '6 - 0', 'two_for_one' => false,
'table_label' => 'Table 2', 'table_number' => 2 }
], 'pairings_reported' => 1
}
] },
Expand All @@ -146,7 +152,8 @@
'player1' => player_with_no_ids('Bob (he/him)'),
'player2' => player_with_no_ids('Charlie (she/her)'),
'policy' => { 'view_decks' => false },
'score_label' => ' - ', 'table_number' => 1, 'two_for_one' => false }
'score_label' => ' - ', 'two_for_one' => false,
'table_label' => 'Game 1', 'table_number' => 1 }
], 'pairings_reported' => 0
}
] }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
let(:round) { create(:round, number: 1, stage:) }
let(:stage) { tournament.current_stage }
let(:tournament) { create(:tournament, swiss_format: :single_sided) }
let(:nil_player) { instance_double(NilPlayer, id: nil, points: 0) }
let(:nil_player) { NilPlayer.new }

before do
allow(NilPlayer).to receive(:new).and_return(nil_player)
Expand Down
Loading

0 comments on commit fddbbd5

Please sign in to comment.