package main import ( // Script Imports "context" "encoding/json" "fmt" "io/ioutil" "math/rand" "os" "slices" "sort" "strconv" "strings" "time" // MongoDB Imports "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) type LineupData struct { Salary []int32 Projection []float64 Team []string Team_count []int32 Secondary []string Secondary_count []int32 Ownership []float64 Players [][]int32 } type Player struct { ID int32 `json:"id"` Name string `json:"name"` Team string `json:"team"` Position string `json:"position"` Salary int32 `json:"salary"` Projection float64 `json:"projection"` Ownership float64 `json:"ownership"` SalaryValue float64 `json:"salary_value"` ProjValue float64 `json:"proj_value"` OwnValue float64 `json:"own_value"` SortValue float64 `json:"sort_value"` Slate string `json:"slate"` } type PlayerSet struct { Players []Player `json:"players"` Maps struct { NameMap map[string]string `json:"name_map"` SalaryMap map[string]int32 `json:"salary_map"` ProjectionMap map[string]float64 `json:"projection_map"` OwnershipMap map[string]float64 `json:"ownership_map"` TeamMap map[string]string `json:"team_map"` } `json:"maps"` } type ProcessedData struct { PlayersMedian PlayerSet `json:"players_median"` } type PlayerData struct { Players []Player NameMap map[int]string } type StrengthResult struct { Index int Data LineupData Error error } type LineupDocument struct { Salary int32 `bson:"salary"` Projection float64 `bson:"proj"` Team string `bson:"Team"` Team_count int32 `bson:"Team_count"` Secondary string `bson:"Secondary"` Secondary_count int32 `bson:"Secondary_count"` Ownership float64 `bson:"Own"` QB int32 `bson:"QB"` RB1 int32 `bson:"RB1"` RB2 int32 `bson:"RB2"` WR1 int32 `bson:"WR1"` WR2 int32 `bson:"WR2"` WR3 int32 `bson:"WR3"` TE int32 `bson:"TE"` FLEX int32 `bson:"FLEX"` DST int32 `bson:"DST"` CreatedAt time.Time `bson:"created_at"` } func loadPlayerData() (*ProcessedData, error) { data, err := ioutil.ReadFile("nfl_go/player_data.json") if err != nil { return nil, fmt.Errorf("failed to read in data: %v", err) } var processedData ProcessedData if err := json.Unmarshal(data, &processedData); err != nil { return nil, fmt.Errorf("failed to parse json: %v", err) } return &processedData, nil } func loadOptimals() (map[string]LineupData, error) { data, err := ioutil.ReadFile("nfl_go/optimal_lineups.json") if err != nil { return nil, fmt.Errorf("failed to parse optimals: %v", err) } type OptimalsJSON struct { Slate string `json:"slate"` Salary int32 `json:"salary"` Projection float64 `json:"projection"` Team string `json:"team"` Team_count int32 `json:"team_count"` Secondary string `json:"secondary"` Secondary_count int32 `json:"secondary_count"` Ownership float64 `json:"ownership"` Players []int32 `json:"players"` } var allOptimals []OptimalsJSON if err := json.Unmarshal(data, &allOptimals); err != nil { return nil, fmt.Errorf("failed to parse optimals JSON: %v", err) } optimalsBySlate := make(map[string]LineupData) for _, optimal := range allOptimals { if _, exists := optimalsBySlate[optimal.Slate]; !exists { optimalsBySlate[optimal.Slate] = LineupData{ Salary: []int32{}, Projection: []float64{}, Team: []string{}, Team_count: []int32{}, Secondary: []string{}, Secondary_count: []int32{}, Ownership: []float64{}, Players: [][]int32{}, } } slateData := optimalsBySlate[optimal.Slate] slateData.Salary = append(slateData.Salary, optimal.Salary) slateData.Projection = append(slateData.Projection, optimal.Projection) slateData.Team = append(slateData.Team, optimal.Team) slateData.Team_count = append(slateData.Team_count, optimal.Team_count) slateData.Secondary = append(slateData.Secondary, optimal.Secondary) slateData.Secondary_count = append(slateData.Secondary_count, optimal.Secondary_count) slateData.Ownership = append(slateData.Ownership, optimal.Ownership) slateData.Players = append(slateData.Players, optimal.Players) optimalsBySlate[optimal.Slate] = slateData } return optimalsBySlate, nil } func appendOptimalLineups(results []LineupData, optimals LineupData) []LineupData { if len(optimals.Salary) == 0 { return results } // Simply append the optimal LineupData to existing results return append(results, optimals) } func convertMapsToInt32Keys(playerSet *PlayerSet) (map[int32]int32, map[int32]float64, map[int32]float64, map[int32]string) { salaryMap := make(map[int32]int32) projMap := make(map[int32]float64) ownMap := make(map[int32]float64) teamMap := make(map[int32]string) for keyStr, value := range playerSet.Maps.SalaryMap { key, err := strconv.Atoi(keyStr) if err != nil { fmt.Printf("Error converting key %s: %v\n", keyStr, err) continue } salaryMap[int32(key)] = value } for keyStr, value := range playerSet.Maps.ProjectionMap { key, err := strconv.Atoi(keyStr) if err != nil { fmt.Printf("Error converting key %s: %v\n", keyStr, err) continue } projMap[int32(key)] = value } for keyStr, value := range playerSet.Maps.OwnershipMap { key, err := strconv.Atoi(keyStr) if err != nil { fmt.Printf("Error converting key %s: %v\n", keyStr, err) continue } ownMap[int32(key)] = value } for keyStr, value := range playerSet.Maps.TeamMap { key, err := strconv.Atoi(keyStr) if err != nil { fmt.Printf("Error converting key %s: %v\n", keyStr, err) continue } teamMap[int32(key)] = value } return salaryMap, projMap, ownMap, teamMap } func processAndFill[T comparable, U any](input []T, valueMap map[T]U) []U { result := make([]U, len(input)) for i, key := range input { if value, exists := valueMap[key]; exists { result[i] = value } else { var zero U result[i] = zero } } return result } func sortChars(strData string) string { runes := []rune(strData) slices.Sort(runes) return string(runes) } func rowMostCommon(row []int) (*int, *int) { if len(row) == 0 { return nil, nil } counts := make(map[int]int) for _, value := range row { counts[value]++ } if len(counts) < 2 { return nil, nil } mostCommon := 0 maxCount := 0 secondMost := 0 secondMax := 0 for value, count := range counts { if count > maxCount { secondMax = maxCount secondMost = mostCommon maxCount = count mostCommon = value } else if count > secondMax && count < maxCount { secondMax = count secondMost = value } } return &mostCommon, &secondMost } func rowBiggestAndSecond(row []int) (int, int) { if len(row) == 0 { return 0, 0 } counts := make(map[int]int) for _, value := range row { counts[value]++ } if len(counts) == 1 { return len(row), 0 } biggestVal := 0 secondBiggestVal := 0 for _, count := range counts { if count > biggestVal { secondBiggestVal = biggestVal biggestVal = count } else if count > secondBiggestVal && count < biggestVal { secondBiggestVal = count } } return biggestVal, secondBiggestVal } func createOverallDFs(players []Player, pos string) PlayerData { var filteredPlayers []Player for _, player := range players { if pos == "FLEX" { if !strings.Contains(player.Position, "QB") && !strings.Contains(player.Position, "DST") { filteredPlayers = append(filteredPlayers, player) } } else { if strings.Contains(player.Position, pos) { filteredPlayers = append(filteredPlayers, player) } } } nameMap := make(map[int]string) for i, player := range filteredPlayers { nameMap[i] = player.Name } return PlayerData{ Players: filteredPlayers, NameMap: nameMap, } } func sumSalaryRows(data [][]int32) []int32 { result := make([]int32, len(data)) for i, row := range data { var sum int32 for _, value := range row { sum += value } result[i] = sum } return result } func sumOwnRows(data [][]float64) []float64 { result := make([]float64, len(data)) for i, row := range data { var sum float64 for _, value := range row { sum += value } result[i] = sum } return result } func sumProjRows(data [][]float64) []float64 { result := make([]float64, len(data)) for i, row := range data { var sum float64 for _, value := range row { sum += value } result[i] = sum } return result } func filterMax[T ~int32 | ~float64](values []T, maxVal T) []int { var validIndicies []int for i, value := range values { if value <= maxVal { validIndicies = append(validIndicies, i) } } return validIndicies } func filterMin[T ~int32 | ~float64](values []T, minVal T) []int { var validIndicies []int for i, value := range values { if value >= minVal { validIndicies = append(validIndicies, i) } } return validIndicies } func sliceByIndicies[T any](data []T, indicies []int) []T { result := make([]T, len(indicies)) for i, idx := range indicies { result[i] = data[idx] } return result } func sortDataByField(data LineupData, field string, ascending bool) LineupData { indicies := make([]int, len(data.Ownership)) for i := range indicies { indicies[i] = i } switch field { case "salary": sort.Slice(indicies, func(i, j int) bool { if ascending { return data.Salary[indicies[i]] < data.Salary[indicies[j]] } return data.Salary[indicies[i]] > data.Salary[indicies[j]] }) case "projection": sort.Slice(indicies, func(i, j int) bool { if ascending { return data.Projection[indicies[i]] < data.Projection[indicies[j]] } return data.Projection[indicies[i]] > data.Projection[indicies[j]] }) case "ownership": sort.Slice(indicies, func(i, j int) bool { if ascending { return data.Ownership[indicies[i]] < data.Ownership[indicies[j]] } return data.Ownership[indicies[i]] > data.Ownership[indicies[j]] }) default: sort.Slice(indicies, func(i, j int) bool { return data.Projection[indicies[i]] > data.Projection[indicies[j]] }) } return LineupData{ Salary: sliceByIndicies(data.Salary, indicies), Projection: sliceByIndicies(data.Projection, indicies), Team: sliceByIndicies(data.Team, indicies), Team_count: sliceByIndicies(data.Team_count, indicies), Secondary: sliceByIndicies(data.Secondary, indicies), Secondary_count: sliceByIndicies(data.Secondary_count, indicies), Ownership: sliceByIndicies(data.Ownership, indicies), Players: sliceByIndicies(data.Players, indicies), } } func combineArrays(qb, rb1, rb2, wr1, wr2, wr3, te, flex, dst []int32) [][]int32 { length := len(qb) result := make([][]int32, length) for i := 0; i < length; i++ { result[i] = []int32{ qb[i], rb1[i], rb2[i], wr1[i], wr2[i], wr3[i], te[i], flex[i], dst[i], } } return result } func createSeedFrames(combinedArrays [][]int32, salaryMap map[int32]int32, projMap map[int32]float64, ownMap map[int32]float64, teamMap map[int32]string, site string) LineupData { salaries := make([][]int32, len(combinedArrays)) projections := make([][]float64, len(combinedArrays)) ownership := make([][]float64, len(combinedArrays)) teamArrays := make([][]string, len(combinedArrays)) for i, row := range combinedArrays { players := row[0:9] playerSalaries := processAndFill(players, salaryMap) playerProjections := processAndFill(players, projMap) playerOwnership := processAndFill(players, ownMap) playerTeams := processAndFill(players, teamMap) salaries[i] = playerSalaries projections[i] = playerProjections ownership[i] = playerOwnership teamArrays[i] = playerTeams } totalSalaries := sumSalaryRows(salaries) totalProjections := sumProjRows(projections) totalOwnership := sumOwnRows(ownership) teamData := make([]string, len(teamArrays)) teamCounts := make([]int32, len(teamArrays)) secondaryData := make([]string, len(teamArrays)) secondaryCounts := make([]int32, len(teamArrays)) for i, teams := range teamArrays { teamMap := make(map[string]int) uniqueTeams := make([]string, 0) for _, team := range teams { if _, exists := teamMap[team]; !exists { teamMap[team] = len(uniqueTeams) uniqueTeams = append(uniqueTeams, team) } } teamInts := make([]int, len(teams)) for j, team := range teams { teamInts[j] = teamMap[team] } mostCommon, secondMost := rowMostCommon(teamInts) if mostCommon != nil { teamData[i] = uniqueTeams[*mostCommon] teamCount, _ := rowBiggestAndSecond(teamInts) teamCounts[i] = int32(teamCount) } if secondMost != nil { secondaryData[i] = uniqueTeams[*secondMost] _, secondaryCount := rowBiggestAndSecond(teamInts) secondaryCounts[i] = int32(secondaryCount) } } var validIndicies []int if site == "DK" { validIndicies = filterMax(totalSalaries, int32(50000)) } else { validIndicies = filterMax(totalSalaries, int32(60000)) } validData := LineupData{ Salary: sliceByIndicies(totalSalaries, validIndicies), Projection: sliceByIndicies(totalProjections, validIndicies), Team: sliceByIndicies(teamData, validIndicies), Team_count: sliceByIndicies(teamCounts, validIndicies), Secondary: sliceByIndicies(secondaryData, validIndicies), Secondary_count: sliceByIndicies(secondaryCounts, validIndicies), Ownership: sliceByIndicies(totalOwnership, validIndicies), Players: sliceByIndicies(combinedArrays, validIndicies), } return sortDataByField(validData, "projection", false) } func calculateQuantile(values []float64, quantile float64) (float64, error) { if len(values) == 0 { return 0, fmt.Errorf("cannot calculate quantile of empty slice") } if quantile < 0 || quantile > 1 { return 0, fmt.Errorf("quantile must be between 0 and 1, got %.2f", quantile) } sorted := make([]float64, len(values)) copy(sorted, values) sort.Float64s(sorted) index := int(float64(len(sorted)-1) * quantile) return sorted[index], nil } func generateUniqueRow(playerIDs []int32, count int, rng *rand.Rand) ([]int32, error) { if count > len(playerIDs) { return nil, fmt.Errorf("cannot generate %d unique values from %d players", count, len(playerIDs)) } shuffled := make([]int32, len(playerIDs)) copy(shuffled, playerIDs) for i := len(shuffled) - 1; i > 0; i-- { j := rng.Intn(i + 1) shuffled[i], shuffled[j] = shuffled[j], shuffled[i] } return shuffled[:count], nil } func generateBaseArrays(qbPlayers, rbPlayers, wrPlayers, tePlayers, flexPlayers, dstPlayers []Player, numRows int, strengthStep float64, rng *rand.Rand) ([][]int32, error) { // DEBUG: Check pool sizes fmt.Printf("DEBUG - Pool sizes: QB=%d, RB=%d, WR=%d, TE=%d, FLEX=%d, DST=%d\n", len(qbPlayers), len(rbPlayers), len(wrPlayers), len(tePlayers), len(flexPlayers), len(dstPlayers)) if len(qbPlayers) == 0 || len(rbPlayers) == 0 || len(wrPlayers) == 0 || len(tePlayers) == 0 || len(flexPlayers) == 0 || len(dstPlayers) == 0 { return nil, fmt.Errorf("one or more position pools is empty: QB=%d, RB=%d, WR=%d, TE=%d, FLEX=%d, DST=%d", len(qbPlayers), len(rbPlayers), len(wrPlayers), len(tePlayers), len(flexPlayers), len(dstPlayers)) } var validArrays [][]int32 attempts := 0 maxAttempts := numRows * 10 for len(validArrays) < numRows && attempts < maxAttempts { attempts++ qb := qbPlayers[rng.Intn(len(qbPlayers))] rb1 := rbPlayers[rng.Intn(len(rbPlayers))] rb2 := rbPlayers[rng.Intn(len(rbPlayers))] wr1 := wrPlayers[rng.Intn(len(wrPlayers))] wr2 := wrPlayers[rng.Intn(len(wrPlayers))] wr3 := wrPlayers[rng.Intn(len(wrPlayers))] te := tePlayers[rng.Intn(len(tePlayers))] flex := flexPlayers[rng.Intn(len(flexPlayers))] dst := dstPlayers[rng.Intn(len(dstPlayers))] if rb1.Name != rb2.Name && rb1.Name != flex.Name && rb2.Name != flex.Name && wr1.Name != wr2.Name && wr1.Name != wr3.Name && wr2.Name != wr3.Name && wr1.Name != flex.Name && wr2.Name != flex.Name && wr3.Name != flex.Name && te.Name != flex.Name { playerIDs := []int32{qb.ID, rb1.ID, rb2.ID, wr1.ID, wr2.ID, wr3.ID, te.ID, flex.ID, dst.ID} validArrays = append(validArrays, playerIDs) } } if len(validArrays) == 0 { return nil, fmt.Errorf("only generated %d valid lineups out of %d requested", len(validArrays), numRows) } return validArrays, nil } func filterPosPlayersInQuantile(players []Player, pos string, strengthStep float64) ([]Player, error) { if len(players) == 0 { return nil, fmt.Errorf("no players provided") } var filteredPlayers []Player for _, player := range players { if pos == "FLEX" { if !strings.Contains(player.Position, "QB") && !strings.Contains(player.Position, "DST") { filteredPlayers = append(filteredPlayers, player) } } else { if strings.Contains(player.Position, pos) { filteredPlayers = append(filteredPlayers, player) } } } ownVals := make([]float64, len(filteredPlayers)) for i, player := range filteredPlayers { ownVals[i] = player.OwnValue } threshold, err := calculateQuantile(ownVals, strengthStep) if err != nil { return nil, err } var filtered []Player for _, player := range filteredPlayers { if player.OwnValue >= threshold { filtered = append(filtered, player) } } if len(filtered) == 0 { return nil, fmt.Errorf("no players meet ownership threshold %.2f", threshold) } return filtered, nil } func processStrengthLevels(players []Player, strengthStep float64, numRows int, rng *rand.Rand, salaryMap map[int32]int32, projMap map[int32]float64, ownMap map[int32]float64, teamMap map[int32]string, site string) (LineupData, error) { qbPlayers, err := filterPosPlayersInQuantile(players, "QB", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter QB players: %v", err) } rbPlayers, err := filterPosPlayersInQuantile(players, "RB", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter RB players: %v", err) } wrPlayers, err := filterPosPlayersInQuantile(players, "WR", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter WR players: %v", err) } tePlayers, err := filterPosPlayersInQuantile(players, "TE", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter TE players: %v", err) } flexPlayers, err := filterPosPlayersInQuantile(players, "FLEX", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter FLEX players: %v", err) } dstPlayers, err := filterPosPlayersInQuantile(players, "DST", strengthStep) if err != nil { return LineupData{}, fmt.Errorf("failed to filter DST players: %v", err) } overallArrays, err := generateBaseArrays(qbPlayers, rbPlayers, wrPlayers, tePlayers, flexPlayers, dstPlayers, numRows, strengthStep, rng) if err != nil { return LineupData{}, fmt.Errorf("failed to generate base arrays: %v", err) } result := createSeedFrames(overallArrays, salaryMap, projMap, ownMap, teamMap, site) return result, nil } func runSeedframeRoutines(players []Player, strengthVars []float64, rowsPerLevel []int, salaryMap map[int32]int32, projMap map[int32]float64, ownMap map[int32]float64, teamMap map[int32]string, site string) ([]LineupData, error) { resultsChan := make(chan StrengthResult, len(strengthVars)) for i, strengthStep := range strengthVars { go func(step float64, rows int, index int) { rng := rand.New(rand.NewSource(time.Now().UnixNano() + int64(index))) result, err := processStrengthLevels(players, step, rows, rng, salaryMap, projMap, ownMap, teamMap, site) resultsChan <- StrengthResult{Index: index, Data: result, Error: err} if err != nil { fmt.Printf("Error in strength level %.2f: %v\n", step, err) } else { fmt.Printf("Completed strength level %.2f with %d lineups\n", step, len(result.Salary)) } }(strengthStep, rowsPerLevel[i], i) } allResults := make([]LineupData, len(strengthVars)) var errors []error successCount := 0 for i := 0; i < len(strengthVars); i++ { result := <-resultsChan if result.Error != nil { errors = append(errors, result.Error) } else { allResults[result.Index] = result.Data successCount++ } } if successCount == 0 { return nil, fmt.Errorf("all %d strength levels failed: %v", len(strengthVars), errors) } var validResults []LineupData for i, result := range allResults { if len(result.Salary) > 0 { validResults = append(validResults, result) } else { fmt.Printf("skipping empty result from strength level %.2f\n", strengthVars[i]) } } fmt.Printf("📊 Successfully processed %d out of %d strength levels\n", len(validResults), len(strengthVars)) return validResults, nil } func printResults(results []LineupData, nameMap map[int32]string) { fmt.Printf("Generated %d strength levels:\n", len(results)) // Combine all results into one big dataset var allSalaries []int32 var allProjections []float64 var allOwnership []float64 var allPlayers [][]int32 for _, result := range results { allSalaries = append(allSalaries, result.Salary...) allProjections = append(allProjections, result.Projection...) allOwnership = append(allOwnership, result.Ownership...) allPlayers = append(allPlayers, result.Players...) } fmt.Printf("Total lineups generated: %d\n", len(allSalaries)) if len(allSalaries) > 0 { // Print top 5 lineups (highest projection) fmt.Printf("\nTop 5 lineups (by projection):\n") for i := 0; i < 5 && i < len(allSalaries); i++ { playerNames := []string{ getPlayerName(allPlayers[i][0], nameMap, "QB"), // QB getPlayerName(allPlayers[i][1], nameMap, "RB1"), // RB1 getPlayerName(allPlayers[i][2], nameMap, "RB2"), // RB2 getPlayerName(allPlayers[i][3], nameMap, "WR1"), // WR1 getPlayerName(allPlayers[i][4], nameMap, "WR2"), // WR2 getPlayerName(allPlayers[i][5], nameMap, "WR3"), // WR3 getPlayerName(allPlayers[i][6], nameMap, "TE"), // TE getPlayerName(allPlayers[i][7], nameMap, "FLEX"), // FLEX getPlayerName(allPlayers[i][8], nameMap, "DST"), // DST } fmt.Printf(" Lineup %d: Salary=%d, Projection=%.2f, Ownership=%.3f\n", i+1, allSalaries[i], allProjections[i], allOwnership[i]) fmt.Printf(" Players: QB=%s, RB1=%s, RB2=%s, WR1=%s, WR2=%s, WR3=%s, TE=%s, FLEX=%s, DST=%s\n", playerNames[0], playerNames[1], playerNames[2], playerNames[3], playerNames[4], playerNames[5], playerNames[6], playerNames[7], playerNames[8]) } // Print bottom 5 lineups (lowest projection) if len(allSalaries) > 5 { fmt.Printf("\nBottom 5 lineups (by projection):\n") start := len(allSalaries) - 5 for i := start; i < len(allSalaries); i++ { // Convert player IDs to names playerNames := []string{ getPlayerName(allPlayers[i][0], nameMap, "QB"), getPlayerName(allPlayers[i][1], nameMap, "RB1"), getPlayerName(allPlayers[i][2], nameMap, "RB2"), getPlayerName(allPlayers[i][3], nameMap, "WR1"), getPlayerName(allPlayers[i][4], nameMap, "WR2"), getPlayerName(allPlayers[i][5], nameMap, "WR3"), getPlayerName(allPlayers[i][6], nameMap, "TE"), getPlayerName(allPlayers[i][7], nameMap, "FLEX"), getPlayerName(allPlayers[i][8], nameMap, "DST"), } fmt.Printf(" Lineup %d: Salary=%d, Projection=%.2f, Ownership=%.3f\n", i+1, allSalaries[i], allProjections[i], allOwnership[i]) fmt.Printf(" Players: QB=%s, RB1=%s, RB2=%s, WR1=%s, WR2=%s, WR3=%s, TE=%s, FLEX=%s, DST=%s\n", playerNames[0], playerNames[1], playerNames[2], playerNames[3], playerNames[4], playerNames[5], playerNames[6], playerNames[7], playerNames[8]) } } } } func removeDuplicates(results []LineupData) []LineupData { seen := make(map[string]bool) var uniqueLineups []LineupData for _, result := range results { for i := 0; i < len(result.Players); i++ { // Create combo string like Python combo := fmt.Sprintf("%d%d%d%d%d%d%d%d%d", result.Players[i][0], result.Players[i][1], result.Players[i][2], result.Players[i][3], result.Players[i][4], result.Players[i][5], result.Players[i][6], result.Players[i][7], result.Players[i][8]) // Sort combo like Python sortedCombo := sortChars(combo) if !seen[sortedCombo] { seen[sortedCombo] = true uniqueLineups = append(uniqueLineups, LineupData{ Salary: []int32{result.Salary[i]}, Projection: []float64{result.Projection[i]}, Team: []string{result.Team[i]}, Team_count: []int32{result.Team_count[i]}, Secondary: []string{result.Secondary[i]}, Secondary_count: []int32{result.Secondary_count[i]}, Ownership: []float64{result.Ownership[i]}, Players: [][]int32{result.Players[i]}, }) } } } if len(uniqueLineups) == 0 { return []LineupData{} } var allSalary []int32 var allProjection []float64 var allTeam []string var allTeamCount []int32 var allSecondary []string var allSecondaryCount []int32 var allOwnership []float64 var allPlayers [][]int32 for _, lineup := range uniqueLineups { allSalary = append(allSalary, lineup.Salary[0]) allProjection = append(allProjection, lineup.Projection[0]) allTeam = append(allTeam, lineup.Team[0]) allTeamCount = append(allTeamCount, lineup.Team_count[0]) allSecondary = append(allSecondary, lineup.Secondary[0]) allSecondaryCount = append(allSecondaryCount, lineup.Secondary_count[0]) allOwnership = append(allOwnership, lineup.Ownership[0]) allPlayers = append(allPlayers, lineup.Players[0]) } return []LineupData{{ Salary: allSalary, Projection: allProjection, Team: allTeam, Team_count: allTeamCount, Secondary: allSecondary, Secondary_count: allSecondaryCount, Ownership: allOwnership, Players: allPlayers, }} } func connectToMongoDB() (*mongo.Client, error) { uri := "mongodb+srv://multichem:Xr1q5wZdXPbxdUmJ@testcluster.lgwtp5i.mongodb.net/?retryWrites=true&w=majority" clientOptions := options.Client(). ApplyURI(uri). SetRetryWrites(true). SetServerSelectionTimeout(10 * time.Second). SetMaxPoolSize(100). SetMinPoolSize(10). SetMaxConnIdleTime(30 * time.Second). SetRetryReads(true) client, err := mongo.Connect(context.TODO(), clientOptions) if err != nil { return nil, fmt.Errorf("failed to connect to MongoDB: %v", err) } err = client.Ping(context.TODO(), nil) if err != nil { return nil, fmt.Errorf("failed to ping mMongoDB %v", err) } fmt.Printf("Connected to MongoDB!") return client, nil } func insertLineupsToMongoDB(client *mongo.Client, results []LineupData, slate string, nameMap map[int32]string, site string, sport string) error { db := client.Database(fmt.Sprintf("%s_Database", sport)) collectionName := fmt.Sprintf("%s_%s_seed_frame_%s", site, sport, slate) // NOTE: change the database here collection := db.Collection(collectionName) err := collection.Drop(context.TODO()) if err != nil { fmt.Printf("Warning: Could not drop collection %s: %v\n", collectionName, err) } var documents []interface{} for _, result := range results { if len(result.Salary) == 0 || len(result.Players) == 0 { fmt.Printf("Warning: Empty result found, skipping\n") continue } for i := 0; i < len(result.Salary); i++ { if len(result.Players[i]) < 9 { fmt.Printf("Warning: Lineup %d has only %d players, expected 9\n", i, len(result.Players[i])) } doc := LineupDocument{ Salary: result.Salary[i], Projection: result.Projection[i], Team: result.Team[i], Team_count: result.Team_count[i], Secondary: result.Secondary[i], Secondary_count: result.Secondary_count[i], Ownership: result.Ownership[i], QB: result.Players[i][0], RB1: result.Players[i][1], RB2: result.Players[i][2], WR1: result.Players[i][3], WR2: result.Players[i][4], WR3: result.Players[i][5], TE: result.Players[i][6], FLEX: result.Players[i][7], DST: result.Players[i][8], CreatedAt: time.Now(), } documents = append(documents, doc) } } if len(documents) == 0 { fmt.Printf("Warning: No documents to insert for slate %s\n", slate) } if len(documents) > 500000 { documents = documents[:500000] } chunkSize := 250000 for i := 0; i < len(documents); i += chunkSize { end := i + chunkSize if end > len(documents) { end = len(documents) } chunk := documents[i:end] for attempt := 0; attempt < 5; attempt++ { ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) opts := options.InsertMany().SetOrdered(false) _, err := collection.InsertMany(ctx, chunk, opts) cancel() if err == nil { fmt.Printf("Successfully inserted chunk %d-%d to %s\n", i, end, collectionName) break } fmt.Printf("Retry %d due to error: %v\n", attempt+1, err) if attempt < 4 { time.Sleep(1 * time.Second) } } if err != nil { return fmt.Errorf("failed to insert chunk %d-%d after 5 attempts: %v", i, end, err) } } fmt.Printf("All documents inserted successfully to %s!\n", collectionName) return nil } func groupPlayersBySlate(players []Player) map[string][]Player { slateGroups := make(map[string][]Player) for _, player := range players { slateGroups[player.Slate] = append(slateGroups[player.Slate], player) } return slateGroups } func getPlayerName(playerID int32, nameMap map[int32]string, position string) string { if name, exists := nameMap[playerID]; exists && name != "" { return name } return fmt.Sprintf("Unknown_%s_%d", position, playerID) } func convertNamesToMaps(playerSet *PlayerSet) map[int32]string { nameMap := make(map[int32]string) for keyStr, value := range playerSet.Maps.NameMap { key, err := strconv.Atoi(keyStr) if err != nil { fmt.Printf("Error coinverting name key %s: %v\n", keyStr, err) continue } nameMap[int32(key)] = value } return nameMap } func main() { site := "DK" sport := "NFL" if len(os.Args) > 1 { site = os.Args[1] } if len(os.Args) > 2 { sport = os.Args[2] } processedData, err := loadPlayerData() if err != nil { fmt.Printf("Error loading data: %v\n", err) return } start := time.Now() strengthVars := []float64{0.01, 0.20, 0.40, 0.60, 0.80} rowsPerLevel := []int{1000000, 1000000, 1000000, 1000000, 1000000} SlateGroups := groupPlayersBySlate(processedData.PlayersMedian.Players) salaryMapJSON, projectionMapJSON, ownershipMapJSON, teamMapJSON := convertMapsToInt32Keys(&processedData.PlayersMedian) nameMap := convertNamesToMaps(&processedData.PlayersMedian) mongoClient, err := connectToMongoDB() if err != nil { fmt.Printf("Error connecting to MongoDB: %v\n", err) return } defer func() { if err := mongoClient.Disconnect(context.TODO()); err != nil { fmt.Printf("Error disconnecting from MongoDB: %v\n", err) } }() optimalsBySlate, err := loadOptimals() if err != nil { fmt.Printf("Warning: Could not load optimal lineups: %v\n", err) optimalsBySlate = make(map[string]LineupData) // Continue with empty optimals } else { totalOptimals := 0 for _, optimals := range optimalsBySlate { totalOptimals += len(optimals.Salary) } fmt.Printf("Loaded %d optimal lineups across all slates\n", totalOptimals) } for slate, players := range SlateGroups { fmt.Printf("Processing slate: %s\n", slate) results, err := runSeedframeRoutines( players, strengthVars, rowsPerLevel, salaryMapJSON, projectionMapJSON, ownershipMapJSON, teamMapJSON, site) if err != nil { fmt.Printf("Error generating mixed lineups for slate %s: %v\n", slate, err) continue } // Get optimal lineups for this specific slate slateOptimals := optimalsBySlate[slate] // Append optimal lineups for this slate finalResults := append([]LineupData{slateOptimals}, results...) exportResults := removeDuplicates(finalResults) exportResults[0] = sortDataByField(exportResults[0], "projection", false) err = insertLineupsToMongoDB(mongoClient, exportResults, slate, nameMap, site, sport) if err != nil { fmt.Printf("Error inserting to MongoDB for slate %s: %v\n", slate, err) continue } printResults(exportResults, nameMap) } // Add this line at the end fmt.Printf("This took %.2f seconds\n", time.Since(start).Seconds()) }