Skip to content

Commit

Permalink
New UX and ability to have straight knockout brackets
Browse files Browse the repository at this point in the history
  • Loading branch information
gitrgoliveira committed Oct 29, 2023
1 parent 0ce450f commit 381b25d
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 102 deletions.
3 changes: 0 additions & 3 deletions TODO.md

This file was deleted.

137 changes: 137 additions & 0 deletions cmd/create-playoffs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package cmd

import (
"bufio"
"fmt"
"log"
"math/rand"
"os"

"github.com/gitrgoliveira/bracket-creator/internal/helper"
"github.com/spf13/cobra"

"github.com/xuri/excelize/v2"
)

type createPlayoffOptions struct {
teamMatches int
filePath string
outputPath string
sanatize bool
determined bool
}

func newCreatePlayoffCmd() *cobra.Command {

o := &createPlayoffOptions{}

cmd := &cobra.Command{
Use: "create-playoffs",
Short: "Creates playoff brackets only",
SilenceUsage: true,
// Args: cobra.ExactArgs(1),
RunE: o.run,
}

cmd.Flags().BoolVarP(&o.determined, "determined", "d", false, "Do not shuffle the names read from the input file")
cmd.PersistentFlags().StringVarP(&o.filePath, "file", "f", "", "file with the list of players/teams")
cmd.PersistentFlags().StringVarP(&o.outputPath, "output", "o", "", "output path for the excel file")
cmd.Flags().BoolVarP(&o.sanatize, "sanatize", "s", false, "Sanatize names into first and last name and capitalize")
cmd.Flags().IntVarP(&o.teamMatches, "team-matches", "t", 0, "create team matches with x players per team (default 0)")

cmd.MarkFlagRequired("file")
cmd.MarkFlagRequired("output")

return cmd
}

func (o *createPlayoffOptions) run(cmd *cobra.Command, args []string) error {

fmt.Fprintf(cmd.OutOrStdout(), "Reading file: %s\n", o.filePath)
file, err := os.Open(o.filePath)
if err != nil {
log.Fatal(err)
}
defer file.Close()

entries := make([]string, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
entry := scanner.Text()
entries = append(entries, entry)
}

entries = helper.RemoveDuplicates(entries)

// Shuffle all entries
if !o.determined {
rand.Shuffle(len(entries), func(i, j int) {
entries[i], entries[j] = entries[j], entries[i]
})
}

players := helper.CreatePlayers(entries)

// Openning the template Excel file.
f, err := excelize.OpenFile("template.xlsx")
if err != nil {
fmt.Println(err)
return nil
}
defer func() {
if err := f.Close(); err != nil {
fmt.Println(err)
}
}()

helper.AddPlayerDataToSheet(f, players, o.sanatize)
// gather all player names
var names []string
if o.sanatize {
for _, player := range players {
names = append(names, player.DisplayName)
}
} else {
for _, player := range players {
names = append(names, player.Name)
}
}
tree := helper.CreateBalancedTree(names, o.sanatize)

depth := helper.CalculateDepth(tree)
fmt.Printf("Tree Depth: %d\n", depth)
helper.PrintLeafNodes(tree, f, "Tree", depth*2, 4, depth, false)

// gathers a list of all of the matches
matches := helper.InOrderTraversal(tree)
matchMapping := helper.FillInMatches(f, matches)
eliminationMatchRounds := make([][]helper.EliminationMatch, depth-1)
// Get all the rounds
for i := depth; i > 1; i-- {
rounds := helper.TraverseRounds(tree, 1, i-1, matchMapping)
eliminationMatchRounds[depth-i] = rounds
fmt.Printf("Elimination matches for round %d: %d\n", i-1, len(eliminationMatchRounds[depth-i]))
}

var matchWinners map[string]helper.MatchWinner
f.DeleteSheet("Pool Draw")
f.DeleteSheet("Pool Matches")
// hurray! they are all winners
matchWinners = helper.ConvertPlayersToWinners(players, o.sanatize)
helper.CreateNamesToPrint(f, players, o.sanatize)

helper.PrintTeamEliminationMatches(f, matchWinners, matchMapping, eliminationMatchRounds, o.teamMatches)

// Save the spreadsheet file
if err := f.SaveAs(o.outputPath); err != nil {
fmt.Println("Error saving Excel file:", err)
return err
}

fmt.Println("Excel file created successfully:", o.outputPath)
return nil
}

func init() {
rootCmd.AddCommand(newCreatePlayoffCmd())
}
102 changes: 37 additions & 65 deletions cmd/create.go → cmd/create-pools.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,55 @@ import (
"github.com/xuri/excelize/v2"
)

type createOptions struct {
type poolOptions struct {
numPlayers int
poolWinners int
teamMatches int
filePath string
outputPath string
roundRobin bool
sanatize bool
determined bool
noPools bool
}

func newCreateCmd() *cobra.Command {
func newCreatePoolCmd() *cobra.Command {

o := &createOptions{}
o := &poolOptions{}

cmd := &cobra.Command{
Use: "create",
Short: "subcommand to create brackets",
Use: "create-pools",
Short: "creates Pool brackets",
SilenceUsage: true,
// Args: cobra.ExactArgs(1),
RunE: o.run,
}

cmd.Flags().BoolVarP(&o.determined, "determined", "d", false, "Do not shuffle the names read from the input file")
cmd.Flags().StringVarP(&o.filePath, "file", "f", "", "file with the list of players/teams")
cmd.Flags().BoolVarP(&o.noPools, "no-pools", "", false, "Do not create pools and have only straight knockouts.")
cmd.Flags().StringVarP(&o.outputPath, "output", "o", "", "output path for the excel file")
cmd.PersistentFlags().BoolVarP(&o.determined, "determined", "d", false, "Do not shuffle the names read from the input file")
cmd.PersistentFlags().StringVarP(&o.filePath, "file", "f", "", "file with the list of players/teams")
cmd.PersistentFlags().StringVarP(&o.outputPath, "output", "o", "", "output path for the excel file")
cmd.Flags().IntVarP(&o.numPlayers, "players", "p", 3, "minimum number of players/teams per pool")
cmd.Flags().IntVarP(&o.poolWinners, "pool-winners", "w", 2, "number of players/teams that can qualify from each pool")
cmd.Flags().BoolVarP(&o.roundRobin, "round-robin", "r", false, "ensure all pools are round robin. Example, in a pool of 4, everyone would fight everyone")
cmd.Flags().BoolVarP(&o.sanatize, "sanatize", "s", false, "Sanatize names into first and last name and capitalize")
cmd.Flags().IntVarP(&o.teamMatches, "team-matches", "t", 0, "create team matches with x players per team (default 0)")

cmd.MarkFlagRequired("file")
cmd.MarkFlagRequired("output")

return cmd
}

func (o *createOptions) run(cmd *cobra.Command, args []string) error {
fmt.Fprintf(cmd.OutOrStdout(), "Reading file: %s\n", o.filePath)
func (o *poolOptions) run(cmd *cobra.Command, args []string) error {

// validation
if o.numPlayers < 2 {
return fmt.Errorf("number of players must be greater than 1")
}
if o.poolWinners >= o.numPlayers {
return fmt.Errorf("pool winners must be less than number of players per pool")
}

fmt.Fprintf(cmd.OutOrStdout(), "Reading file: %s\n", o.filePath)
file, err := os.Open(o.filePath)
if err != nil {
log.Fatal(err)
Expand All @@ -74,11 +85,7 @@ func (o *createOptions) run(cmd *cobra.Command, args []string) error {
}

players := helper.CreatePlayers(entries)
var pools []helper.Pool

if !o.noPools {
pools = helper.CreatePools(players, o.numPlayers)
}
pools := helper.CreatePools(players, o.numPlayers)

// Openning the template Excel file.
f, err := excelize.OpenFile("template.xlsx")
Expand All @@ -92,40 +99,18 @@ func (o *createOptions) run(cmd *cobra.Command, args []string) error {
}
}()

if o.noPools {
helper.AddPlayerDataToSheet(f, players, o.sanatize)
} else {
helper.AddPoolDataToSheet(f, pools, o.sanatize)
}
var tree *helper.Node
helper.AddPoolDataToSheet(f, pools, o.sanatize)

if !o.noPools {
helper.AddPoolsToSheet(f, pools)
finals := helper.GenerateFinals(pools)
tree = helper.CreateBalancedTree(finals, false)
} else {
// gather all player names
var names []string
if o.sanatize {
for _, player := range players {
names = append(names, player.DisplayName)
}
} else {
for _, player := range players {
names = append(names, player.Name)
}
}
tree = helper.CreateBalancedTree(names, o.sanatize)
}
helper.AddPoolsToSheet(f, pools)
finals := helper.GenerateFinals(pools, o.poolWinners)
tree := helper.CreateBalancedTree(finals, false)

// helper.calc
depth := helper.CalculateDepth(tree)
fmt.Printf("Tree Depth: %d\n", depth)
helper.PrintLeafNodes(tree, f, "Tree", depth*2, 4, depth)

if !o.noPools {
helper.AddPoolsToTree(f, "Tree", pools)
}
helper.PrintLeafNodes(tree, f, "Tree", depth*2, 4, depth, true)
helper.PrintLeafNodes(tree, f, "Tree", depth*2, 4, depth, true)
helper.AddPoolsToTree(f, "Tree", pools)

// gathers a list of all of the matches
matches := helper.InOrderTraversal(tree)
Expand All @@ -138,27 +123,14 @@ func (o *createOptions) run(cmd *cobra.Command, args []string) error {
fmt.Printf("Elimination matches for round %d: %d\n", i-1, len(eliminationMatchRounds[depth-i]))
}

if !o.noPools {
if o.roundRobin {
helper.CreatePoolRoundRobinMatches(pools)
} else {
helper.CreatePoolMatches(pools)
}
}

var matchWinners map[string]helper.MatchWinner
if o.noPools {
f.DeleteSheet("Pool Draw")
f.DeleteSheet("Pool Matches")
// hurray! they are all winners
matchWinners = helper.ConvertPlayersToWinners(players, o.sanatize)
helper.CreateNamesToPrint(f, players, o.sanatize)

if o.roundRobin {
helper.CreatePoolRoundRobinMatches(pools)
} else {
matchWinners = helper.PrintPoolMatches(f, pools, o.teamMatches)
helper.CreateNamesWithPoolToPrint(f, pools, o.sanatize)
helper.CreatePoolMatches(pools)
}

matchWinners := helper.PrintPoolMatches(f, pools, o.teamMatches, o.poolWinners)
helper.CreateNamesWithPoolToPrint(f, pools, o.sanatize)
helper.PrintTeamEliminationMatches(f, matchWinners, matchMapping, eliminationMatchRounds, o.teamMatches)

// Save the spreadsheet file
Expand All @@ -172,5 +144,5 @@ func (o *createOptions) run(cmd *cobra.Command, args []string) error {
}

func init() {
rootCmd.AddCommand(newCreateCmd())
rootCmd.AddCommand(newCreatePoolCmd())
}
5 changes: 2 additions & 3 deletions internal/helper/excel.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func FillInMatches(f *excelize.File, matches []string) map[string]int {
return mapping
}

func PrintPoolMatches(f *excelize.File, pools []Pool, teamMatches int) map[string]MatchWinner {
func PrintPoolMatches(f *excelize.File, pools []Pool, teamMatches int, numWinners int) map[string]MatchWinner {

matchWinners := make(map[string]MatchWinner)
sheetName := "Pool Matches"
Expand Down Expand Up @@ -127,7 +127,7 @@ func PrintPoolMatches(f *excelize.File, pools []Pool, teamMatches int) map[strin
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", resultCol, poolRow), fmt.Sprintf("%d. ", result))
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", endColName, poolRow), fmt.Sprintf("%s%d", endColName, poolRow), getBorderStyleBottom(f))

if result <= 2 {
if result <= numWinners {
matchWinners[fmt.Sprintf("%s.%d", pool.PoolName, result)] = MatchWinner{
sheetName: sheetName,
cell: fmt.Sprintf("%s%d", endColName, poolRow),
Expand Down Expand Up @@ -206,7 +206,6 @@ func PrintEliminationMatches(f *excelize.File, poolMatchWinners map[string]Match
matchRow++

//////////////////////////////////////
// eliminationMatch.Left checks if it is a pool winner
startCell = startColName + fmt.Sprint(matchRow)
var leftCellValue, rightCellValue string

Expand Down
Loading

0 comments on commit 381b25d

Please sign in to comment.