slashinfty / tournament-organizer

JavaScript library for running tournaments
https://slashinfty.github.io/tournament-organizer/
MIT License
51 stars 18 forks source link

Seeding and phases questions #20

Closed omikulcik closed 11 months ago

omikulcik commented 12 months ago

Hi @slashinfty, thank you for creating this library. It is really helpful! I just have a few questions and it would be really kind of you to help me.

  1. Does the blossom algorithm matchmake players in a different way? I have tried apps like Challonge that when you have six players seeded from 1-6 it makes pairs 1-3, 2-5, 3-6 and that is what we expected. I tried assigning values to players and seting the sorting to both ascending and descending and the matches were paired like 1-2, 3-4, 5-6. See the output bellow.
{
  "id": "kcV8hJnA7TbB",
  "name": "Turnajek",
  "status": "stage-one",
  "round": 1,
  "players": [
    {
      "id": "1",
      "name": "T1",
      "active": true,
      "value": 1,
      "matches": [
        {
          "id": "ZYvoVokIK2eG",
          "round": 1,
          "match": 2,
          "active": true,
          "bye": false,
          "player1": {
            "id": "2",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "1",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T1"
      }
    },
    {
      "id": "2",
      "name": "T2",
      "active": true,
      "value": 2,
      "matches": [
        {
          "id": "ZYvoVokIK2eG",
          "round": 1,
          "match": 2,
          "active": true,
          "bye": false,
          "player1": {
            "id": "2",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "1",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T2"
      }
    },
    {
      "id": "3",
      "name": "T3",
      "active": true,
      "value": 3,
      "matches": [
        {
          "id": "lClBejYglpDn",
          "round": 1,
          "match": 3,
          "active": true,
          "bye": false,
          "player1": {
            "id": "3",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "4",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T3"
      }
    },
    {
      "id": "4",
      "name": "T4",
      "active": true,
      "value": 4,
      "matches": [
        {
          "id": "lClBejYglpDn",
          "round": 1,
          "match": 3,
          "active": true,
          "bye": false,
          "player1": {
            "id": "3",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "4",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T4"
      }
    },
    {
      "id": "5",
      "name": "T5",
      "active": true,
      "value": 5,
      "matches": [
        {
          "id": "OU4AxAJbfnmC",
          "round": 1,
          "match": 1,
          "active": true,
          "bye": false,
          "player1": {
            "id": "6",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "5",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T5"
      }
    },
    {
      "id": "6",
      "name": "T6",
      "active": true,
      "value": 6,
      "matches": [
        {
          "id": "OU4AxAJbfnmC",
          "round": 1,
          "match": 1,
          "active": true,
          "bye": false,
          "player1": {
            "id": "6",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "player2": {
            "id": "5",
            "win": 0,
            "loss": 0,
            "draw": 0
          },
          "path": {
            "win": null,
            "loss": null
          },
          "meta": {}
        }
      ],
      "meta": {
        "name": "T6"
      }
    }
  ],
  "matches": [
    {
      "id": "OU4AxAJbfnmC",
      "round": 1,
      "match": 1,
      "active": true,
      "bye": false,
      "player1": {
        "id": "6",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "player2": {
        "id": "5",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "path": {
        "win": null,
        "loss": null
      },
      "meta": {
        "dbId": 50
      }
    },
    {
      "id": "ZYvoVokIK2eG",
      "round": 1,
      "match": 2,
      "active": true,
      "bye": false,
      "player1": {
        "id": "2",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "player2": {
        "id": "1",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "path": {
        "win": null,
        "loss": null
      },
      "meta": {
        "dbId": 51
      }
    },
    {
      "id": "lClBejYglpDn",
      "round": 1,
      "match": 3,
      "active": true,
      "bye": false,
      "player1": {
        "id": "3",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "player2": {
        "id": "4",
        "win": 0,
        "loss": 0,
        "draw": 0
      },
      "path": {
        "win": null,
        "loss": null
      },
      "meta": {
        "dbId": 52
      }
    }
  ],
  "colored": false,
  "sorting": "descending",
  "scoring": {
    "bestOf": 1,
    "win": 1,
    "draw": 0.5,
    "loss": 0,
    "bye": 1,
    "tiebreaks": []
  },
  "stageOne": {
    "format": "swiss",
    "consolation": false,
    "rounds": 3,
    "maxPlayers": 0
  },
  "stageTwo": {
    "format": "single-elimination",
    "consolation": false,
    "advance": {
      "method": "rank",
      "value": 3
    }
  },
  "meta": {}
}
  1. Do you plan to add a feature to play only one phase or more than two phases?
  2. Can it somehow be ensured that BYE is assigned to either last seeded player or a random player?

Thank you very much! If any of these is not possible I may help with the development.

slashinfty commented 12 months ago

Thanks for the questions.

I want to quickly answer number 2: you can have a tournament be one phase by setting stageTwo.format = null (which is the default). I have no plans to make it a variable number of stages beyond 2.

I will answer your other questions later today when time permits. I want to describe the pairing algorithm in sufficient detail.

slashinfty commented 12 months ago

So the Blossom algorithm is just a way to maximize matching in a weighted graph. In our application, each player is a vertex, and each potential pairing is a path. For it to work as a Swiss pairing system, weights are assigned based on several factors:

To address your remaining questions:

1 - Players are more likely to be paired with others who are close in sorted value - sometimes this is an assigned rank, sometimes it is a rating of skill. Pairing in halves, as you describe Challonge doing, it easy to implement for round 1, but more difficult for future rounds (using the system as it is currently built). I can make an option for the first round.

3 - I do think the lowest valued player in the first round should get the bye, and I think having a separate procedure for round 1 might be in order.

Let me know what you think!

omikulcik commented 11 months ago

Thank you for your explanation. I did a further research and it seems that the Blossom algorithm is the right one for chess and I will be running petanque etc. tournaments that use different algorithm. Nevermind, I will probably code my own algo.

  1. Great, tested and it works. Thanks! :)
  2. That is correct.

Thank you very much for your time and kindness.

slashinfty commented 11 months ago

To be clear, I didn't design the pairing algorithm (or choose the blossom algorithm) based on chess - in fact, trading card games like Magic: the Gathering are my main experience. The type of pairing you described and showed, with 1 vs 4, 2 vs 5, etc, is actually how chess does pairings. So what you want is closely related to chess.