R/league.R

Defines functions getSeriesOdds build_past_predictions cleanupPredictionsFile recordTodaysPredictions getAllHomeAwayOdds playoffSolverEngine getCompletedSeries single_series_solver reseedTwoTeams simulatePlayoffs playoffSeriesOdds randomSeriesWinner playoffWin sim_engine loopless_sim compile_predictions simulateSeasonParallel todayOdds buildStats

Documented in buildStats cleanupPredictionsFile compile_predictions getSeriesOdds loopless_sim playoffSeriesOdds playoffSolverEngine playoffWin randomSeriesWinner recordTodaysPredictions sim_engine simulatePlayoffs simulateSeasonParallel single_series_solver todayOdds

# league stuff: tables, standings, stats, etc.

#' Bulid Stats Table
#'
#' @param scores scores to use to build stats table
#'
#' @return a stats table (as tibble)
#' @export
buildStats <- function(scores) {
  scores <- droplevels(scores)
  teamlist <- sort(unique(c(as.character(scores$HomeTeam), as.character(scores$AwayTeam))))

  # remainderSeasonDC(nsims=10, cores=1, scores=scor, schedule = sched, regress = TRUE) testing passes results instead of home & Away goals
  if ("HomeGoals" %in% colnames(scores)) {
    tmp1 <- scores %>%
      dplyr::group_by(.data$HomeTeam) %>%
      dplyr::summarise(
        GP = dplyr::n(),
        W = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == ""),
        OTW = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == "OT"),
        SOW = sum(.data$HomeGoals > .data$AwayGoals & .data$OTStatus == "SO"),
        OTL = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == "OT"),
        SOL = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == "SO"),
        L = sum(.data$HomeGoals < .data$AwayGoals & .data$OTStatus == ""),
        P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
      ) %>%
      dplyr::ungroup()
    tmp2 <- scores %>%
      dplyr::group_by(.data$AwayTeam) %>%
      dplyr::summarise(
        GP = dplyr::n(),
        W = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == ""),
        OTW = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == "OT"),
        SOW = sum(.data$AwayGoals > .data$HomeGoals & .data$OTStatus == "SO"),
        OTL = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == "OT"),
        SOL = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == "SO"),
        L = sum(.data$AwayGoals < .data$HomeGoals & .data$OTStatus == ""),
        P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
      ) %>%
      dplyr::ungroup()
  } else if ("Result" %in% colnames(scores)) {
    tmp1 <- scores %>%
      dplyr::group_by(.data$HomeTeam) %>%
      dplyr::summarise(
        GP = dplyr::n(),
        W = sum(.data$Result == 1),
        OTW = sum(.data$Result == 0.75),
        SOW = sum(.data$Result == 0.60),
        OTL = sum(.data$Result == 0.40),
        SOL = sum(.data$Result == 0.25),
        L = sum(.data$Result == 0),
        P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
      ) %>%
      dplyr::ungroup()
    tmp2 <- scores %>%
      dplyr::group_by(.data$AwayTeam) %>%
      dplyr::summarise(
        GP = dplyr::n(),
        W = sum(.data$Result == 0),
        OTW = sum(.data$Result == 0.25),
        SOW = sum(.data$Result == 0.40),
        OTL = sum(.data$Result == 0.60),
        SOL = sum(.data$Result == 0.75),
        L = sum(.data$Result == 1),
        P = as.numeric(.data$W * 2 + .data$OTW * 2 + .data$SOW * 2 + .data$OTL + .data$SOL)
      ) %>%
      dplyr::ungroup()
  } else {
    stop("Scores must contain home & away goal info or result info.")
  }

  team_stats <- data.frame(
    Team = teamlist,
    GP = tmp1$GP + tmp2$GP,
    Points = tmp1$P + tmp2$P,
    W = tmp1$W + tmp2$W,
    L = tmp1$L + tmp2$L,
    OTL = tmp1$OTL + tmp2$OTL,
    OTW = tmp1$OTW + tmp2$OTW,
    SOL = tmp1$SOL + tmp2$SOL,
    SOW = tmp1$SOW + tmp2$SOW,
    stringsAsFactors = FALSE
  )

  team_stats <- team_stats %>%
    dtplyr::lazy_dt() %>%
    dplyr::mutate(
      PointPercent = .data$Points / .data$GP,
      ROW = .data$W + .data$OTW,
      ROSW = .data$W + .data$OTW + .data$SOW,
      Rand = stats::runif(dplyr::n())
    ) %>%
    dplyr::mutate(
      Rank = order(order(-.data$Points, -.data$PointPercent, -.data$W, -.data$ROW, -.data$ROSW, .data$Rand)), # include Random for random ties sorting, else Anaheim will always beat Vegas if they're tied.
      Conf = getTeamConferences(.data$Team), # convenience data, dropped later
      Div = getTeamDivisions(.data$Team)
    ) %>%
    dplyr::group_by(.data$Conf) %>%
    dplyr::mutate(ConfRank = rank(.data$Rank, ties.method = "random")) %>%
    dplyr::ungroup() %>%
    dplyr::group_by(.data$Div) %>%
    dplyr::mutate(DivRank = rank(.data$Rank, ties.method = "random")) %>%
    dplyr::ungroup() %>%
    dplyr::mutate(Playoffs = ifelse(.data$DivRank <= 3, 1, 0)) %>%
    dplyr::group_by(.data$Conf, .data$Playoffs) %>%
    dplyr::mutate(Playoffs = ifelse(.data$Rank %in% utils::tail(sort(.data$Rank), 2), 1, .data$Playoffs)) %>% ## Renaming top two playoff teams as 'in' doesn't matter, because they're in already
    dplyr::ungroup() %>%
    dplyr::select(-c("Conf", "Div", "PointPercent", "ROW", "ROSW", "Rand")) %>%
    tibble::as_tibble()

  return(team_stats)
}


#' Today's Odds
#'
#' @description Determine today's games' odds (if today has games), or a specified date's odds
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param today The date for which you want game odds
#' @param schedule The schedule, default to internal schedule
#' @param expected_mean the mean lambda & mu, used only for regression
#' @param season_percent the percent complete of the season, used for regression
#' @param include_xG Whether to include xG values in reported odds
#'
#' @return a data frame of HomeTeam, AwayTeam, HomeWin, AwayWin, Draw, or NULL if no games today
#' @export
#'
#' @examples todayOdds(today = as.Date("2019-11-01"))
todayOdds <- function(params = NULL, today = Sys.Date(), schedule = HockeyModel::schedule, expected_mean = NULL, season_percent = NULL, include_xG = FALSE) {
  return(todayDC(params = params, today = today, schedule = schedule, expected_mean = expected_mean, season_percent = season_percent, include_xG = include_xG))
}


#' Simulate the remainder of the season
#'
#' @param scores Past (historical) season scores. Defaults to HockeyModel::Scores
#' @param schedule Future unplayed games. Defaults to HockeyModel::schedule
#' @param nsims number of simulations to run
#' @param cores number of cores to use in parallel.
#' @param progress whether to show a progress bar.
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a data frame of results
#' @export
simulateSeasonParallel <- function(scores = HockeyModel::scores, nsims = 10000, schedule = HockeyModel::schedule, cores = NULL, progress = FALSE, params = NULL) {
  teamlist <- c()
  if (!is.null(scores)) {
    season_sofar <- scores[scores$Date > as.Date(getSeasonStartDate()), ]
    season_sofar <- season_sofar[, c("Date", "HomeTeam", "AwayTeam", "Result")]
  } else {
    season_sofar <- NULL
  }

  teamlist <- c(teamlist, sort(unique(c(as.character(schedule$Home), as.character(schedule$Away)))))

  cores <- parseCores(cores)

  odds_table <- remainderSeasonDC(scores = scores, schedule = schedule, params = params, odds = T)

  odds_table$HOT <- extraTimeSolver(odds_table$HomeWin, odds_table$AwayWin, odds_table$Draw)[, 2]
  odds_table$AOT <- extraTimeSolver(odds_table$HomeWin, odds_table$AwayWin, odds_table$Draw)[, 3]

  if (cores > 1 & requireNamespace("parallel", quietly = TRUE)) {
    `%dopar%` <- foreach::`%dopar%` # This hack passes R CMD CHK
    cl <- parallel::makeCluster(cores)
    doSNOW::registerDoSNOW(cl)
    if (progress) {
      pb <- utils::txtProgressBar(max = nsims, style = 3)
      progress <- function(n) utils::setTxtProgressBar(pb, n)
      opts <- list(progress = progress)
    } else {
      opts <- list()
    }
    all_results <- foreach::foreach(i = 1:nsims, .combine = "rbind", .options.snow = opts, .packages = c("HockeyModel")) %dopar% {
      # Generate Games results once
      tmp <- odds_table
      tmp$res1 <- stats::runif(n = nrow(tmp))
      tmp$res2 <- stats::runif(n = nrow(tmp))
      tmp$Result <- 1 * (as.numeric(tmp$res1 < tmp$HomeWin)) +
        0.75 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 < 0.6858606)) +
        0.6 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 > 0.6858606)) +
        0.4 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 > 0.6858606)) +
        0.25 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 < 0.6858606)) +
        0

      tmp$HomeWin <- NULL
      tmp$AwayWin <- NULL
      tmp$HOT <- NULL
      tmp$AOT <- NULL
      tmp$Draw <- NULL
      tmp$res1 <- NULL
      tmp$res2 <- NULL

      tmp <- rbind(season_sofar, tmp)
      # Make the season table
      table <- buildStats(tmp)
      table$SimNo <- i

      table
    }
    if (progress) {
      close(pb)
    }
    parallel::stopCluster(cl)
    gc(verbose = FALSE)
  } else {
    if (cores > 1 & !requireNamespace("parallel", quietly = TRUE)) {
      message("Parallel processing is only available if the parallels package is installed.")
    }
    all_results <- list()
    for (i in 1:nsims) {
      tmp <- odds_table
      tmp$HOT <- extraTimeSolver(tmp$HomeWin, tmp$AwayWin, tmp$Draw)[, 2]
      tmp$AOT <- extraTimeSolver(tmp$HomeWin, tmp$AwayWin, tmp$Draw)[, 3]
      tmp$res1 <- stats::runif(n = nrow(tmp))
      tmp$res2 <- stats::runif(n = nrow(tmp))
      tmp$Result <- 1 * (as.numeric(tmp$res1 < tmp$HomeWin)) +
        0.75 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 < 0.6858606)) +
        0.6 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT)) * as.numeric(tmp$res2 > 0.6858606)) +
        0.4 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 > 0.6858606)) +
        0.25 * (as.numeric(tmp$res1 > tmp$HomeWin & tmp$res1 < (tmp$HomeWin + tmp$HOT + tmp$AOT)) * as.numeric(tmp$res2 < 0.6858606)) +
        0

      tmp$HomeWin <- NULL
      tmp$AwayWin <- NULL
      tmp$HOT <- NULL
      tmp$AOT <- NULL
      tmp$Draw <- NULL
      tmp$res1 <- NULL
      tmp$res2 <- NULL

      tmp <- rbind(season_sofar, tmp)
      # Make the season table
      table <- buildStats(tmp)
      table$SimNo <- i
      all_results[[i]] <- table
    }
    all_results <- dplyr::bind_rows(all_results)
  }

  summary_results <- all_results %>%
    dtplyr::lazy_dt() %>%
    dplyr::group_by(.data$Team) %>%
    dplyr::summarise(
      Playoffs = mean(.data$Playoffs),
      meanPoints = mean(.data$Points, na.rm = TRUE),
      maxPoints = max(.data$Points, na.rm = TRUE),
      minPoints = min(.data$Points, na.rm = TRUE),
      meanWins = mean(.data$W, na.rm = TRUE),
      maxWins = max(.data$W, na.rm = TRUE),
      Presidents = sum(.data$Rank == 1) / dplyr::n(),
      meanRank = mean(.data$Rank, na.rm = TRUE),
      bestRank = min(.data$Rank, na.rm = TRUE),
      # meanConfRank = mean(.data$ConfRank, na.rm = TRUE),
      # bestConfRank = min(.data$ConfRank, na.rm = TRUE),
      meanDivRank = mean(.data$DivRank, na.rm = TRUE),
      bestDivRank = min(.data$DivRank, na.rm = TRUE),
      sdPoints = stats::sd(.data$Points, na.rm = TRUE),
      sdWins = stats::sd(.data$W, na.rm = TRUE),
      sdRank = stats::sd(.data$Rank, na.rm = TRUE),
      # sdConfRank = stats::sd(.data$ConfRank, na.rm = TRUE),
      sdDivRank = stats::sd(.data$DivRank, na.rm = TRUE)
    ) %>%
    tibble::as_tibble()


  return(list(summary_results = summary_results, raw_results = all_results))
}

#' Compile predictions to one object
#'
#' @description compiles predictions from a group of .RDS files to one data.frame
#' @param dir Directory holding the prediction .RDS files.
#'
#' @return a data frame.
#' @export
compile_predictions <- function(dir = getOption("HockeyModel.prediction.path")) {
  # Find the files
  filelist <- list.files(path = dir)
  pdates <- substr(filelist, 1, 10) # gets the dates list of prediction
  pdates <- pdates[pdates != "graphics"]
  all_predictions <- lapply(pdates, function(f) readRDS(file.path(dir, (paste0(f, "-predictions.RDS"))))) # Read all the files
  names(all_predictions) <- pdates
  all_predictions <- dplyr::bind_rows(all_predictions, .id = "predictionDate")
  return(all_predictions)
}


#' 'Loopless' simulation
#'
#' @param nsims number of simulations to run (approximate)
#' @param cores number of cores in parallel to process
#' @param schedule games to play
#' @param scores Season to this point
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param season_sofar The results of the season to date
#' @param likelihood_graphic whether to create a likelihood graphic
#' @param odds_table a table of odds for all games in schedule. Null, unless provided. Should be similar to the output of `remainderSeasonDC(odds=TRUE)`,
#' which is a data.frame of HomeTeam, AwayTeam, HomeWin, AwayWin, Draw, GameID and Date
#'
#' @return a two member list, of all results and summary results
#' @export
loopless_sim <- function(nsims = 1e5, cores = NULL, schedule = HockeyModel::schedule, scores = HockeyModel::scores, params = NULL, season_sofar = NULL, likelihood_graphic = TRUE, odds_table = NULL) {
  params <- parse_dc_params(params)

  cores <- parseCores(cores)

  nsims <- floor(nsims / cores)

  schedule <- schedule[!(schedule$GameID %in% scores$GameID), ]

  schedule <- add_postponed_to_schedule_end(schedule)

  if (is.null(odds_table) | !(all(c("HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw", "GameID", "Date") %in% colnames(odds_table)))) {
    odds_table <- remainderSeasonDC(scores = scores, schedule = schedule, params = params, nsims = nsims, odds = T)
  }

  season_sofar <- scores[scores$Date >= as.Date(getSeasonStartDate(getSeason(schedule[, "Date"][1]))), ]

  if (nrow(season_sofar) > 0) {
    season_sofar <- season_sofar[, c("Date", "HomeTeam", "AwayTeam", "Result", "GameID")]
    odds_table <- odds_table[!(odds_table$GameID %in% season_sofar$GameID), ]
    all_season <- dplyr::bind_rows(season_sofar, odds_table)
  } else {
    all_season <- odds_table
    all_season$Result <- NA
  }

  oddsseason <- extraTimeSolver(all_season$HomeWin, all_season$AwayWin, 1 - (all_season$HomeWin + all_season$AwayWin))
  all_season$HomeOT <- oddsseason[, 2] * 0.6858606
  all_season$HomeSO <- oddsseason[, 2] * 0.3141394
  all_season$AwaySO <- oddsseason[, 3] * 0.3141394
  all_season$AwayOT <- oddsseason[, 3] * 0.6858606

  rm(oddsseason, season_sofar, odds_table)

  if (cores == 1 | !requireNamespace("parallel", quietly = TRUE)) {
    if (cores > 1) {
      message("Multi-core processing requires the parallel package.")
    }
    # for testing only, really.
    all_results <- sim_engine(all_season = all_season, nsims = nsims, params = params)
  } else {
    # this fixes CRAN checks
    `%dopar%` <- foreach::`%dopar%`
    # `%do%` <- foreach::`%do%`

    cl <- parallel::makeCluster(cores)
    doSNOW::registerDoSNOW(cl)

    # Ram management issues. Send smaller chunks more often, hopefully this helps.
    all_results <- foreach::foreach(i = seq_along(1:(cores * 100)), .combine = "rbind", .packages = "HockeyModel") %dopar% {
      all_results <- sim_engine(all_season = all_season, nsims = ceiling(nsims / 100), params = params)
      return(all_results)
    }

    parallel::stopCluster(cl)
    gc(verbose = FALSE)
  }

  summary_results <- all_results %>%
    # dtplyr::lazy_dt() %>%
    dplyr::group_by(.data$Team) %>%
    dplyr::summarise(
      Playoffs = mean(.data$Playoffs),
      meanPoints = mean(.data$Points, na.rm = TRUE),
      maxPoints = max(.data$Points, na.rm = TRUE),
      minPoints = min(.data$Points, na.rm = TRUE),
      meanWins = mean(.data$W, na.rm = TRUE),
      maxWins = max(.data$W, na.rm = TRUE),
      Presidents = sum(.data$Rank == 1) / dplyr::n(),
      meanRank = mean(.data$Rank, na.rm = TRUE),
      bestRank = min(.data$Rank, na.rm = TRUE),
      meanConfRank = mean(.data$ConfRank, na.rm = TRUE),
      bestConfRank = min(.data$ConfRank, na.rm = TRUE),
      meanDivRank = mean(.data$DivRank, na.rm = TRUE),
      bestDivRank = min(.data$DivRank, na.rm = TRUE),
      sdPoints = stats::sd(.data$Points, na.rm = TRUE),
      sdWins = stats::sd(.data$W, na.rm = TRUE),
      sdRank = stats::sd(.data$Rank, na.rm = TRUE),
      sdConfRank = stats::sd(.data$ConfRank, na.rm = TRUE),
      sdDivRank = stats::sd(.data$DivRank, na.rm = TRUE),
      p_rank1 = sum(.data$ConfRank == 1 & .data$DivRank == 1) / dplyr::n(),
      p_rank2 = sum(.data$ConfRank != 1 & .data$DivRank == 1) / dplyr::n(),
      # Solving 3 & 4 & 5 & 6 doesn't *really* matter, because 3/4 play the 5/6 within their own division.
      # In 2nd round, 1/8 or 2/7 play the 3/6 or 4/5 from their own division. No re-seeding occurs.
      # See: https://en.wikipedia.org/wiki/Stanley_Cup_playoffs#Current_format
      p_rank_34 = sum(.data$DivRank == 2) / dplyr::n(),
      p_rank_56 = sum(.data$DivRank == 3) / dplyr::n(),
      p_rank7 = sum(.data$Wildcard == 1) / dplyr::n(),
      p_rank8 = sum(.data$Wildcard == 2) / dplyr::n()
    ) %>%
    tibble::as_tibble()

  if (likelihood_graphic) {
    plot_point_likelihood(preds = all_results)
  }

  return(list(summary_results = summary_results, raw_results = all_results))
}

#' Simulation engine to be parallelized or used in single core
#'
#' @param all_season One seasons' scores & odds schedule
#' @param nsims Number of simulations to run
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return results of `nsims` season simulations, as one long data frame score table.
#' @export
sim_engine <- function(all_season, nsims, params = NULL) {
  params <- parse_dc_params(params)

  season_length <- nrow(all_season)

  # multi_season<-dplyr::bind_rows(replicate(nsims, all_season[,c('HomeTeam', 'AwayTeam', 'Result', 'GameID')], simplify = FALSE))
  # multi_season$sim<-rep(1:nsims, each = season_length)

  resultslist <- list()

  # TODO: This can be vectorized or delooped by doing Result prediction on long_season?
  for (g in all_season$GameID) {
    if (is.na(all_season[all_season$GameID == g, ]$Result)) {
      odds <- as.vector(all_season[all_season$GameID == g, c("HomeWin", "HomeOT", "HomeSO", "AwaySO", "AwayOT", "AwayWin")])
      # multi_season[multi_season$GameID == g,]$Result <- sampleResult(odds[[1]], odds[[2]], odds[[3]], odds[[4]], odds[[5]], odds[[6]], size=nsims)
      resultslist[[as.character(g)]] <- sampleResult(odds[[1]], odds[[2]], odds[[3]], odds[[4]], odds[[5]], odds[[6]], size = nsims)
    } else {
      resultslist[[as.character(g)]] <- rep(all_season[all_season$GameID == g, ]$Result, nsims)
    }
  }

  long_season <- data.frame(
    Team = c(rep(all_season$HomeTeam, each = nsims), rep(all_season$AwayTeam, each = nsims)),
    SimNo = c(rep(1:nsims, season_length), rep(1:nsims, season_length)),
    Result = c(unlist(resultslist), 1 - unlist(resultslist))
  )

  rm(resultslist)

  all_results <- long_season %>%
    dtplyr::lazy_dt() %>%
    dplyr::group_by(.data$SimNo, .data$Team) %>%
    dplyr::summarise(
      W = sum(.data$Result == 1),
      OTW = sum(.data$Result == 0.75),
      SOW = sum(.data$Result == 0.6),
      SOL = sum(.data$Result == 0.4),
      OTL = sum(.data$Result == 0.25),
      L = sum(.data$Result == 0)
    ) %>%
    as.data.frame()

  rm(long_season)


  all_results$Points <- all_results$W * 2 + all_results$OTW * 2 + all_results$SOW * 2 + all_results$OTL + all_results$SOL

  all_results$Conference <- unlist(getTeamConferences(all_results$Team))
  all_results$Division <- getTeamDivisions(all_results$Team)
  all_results$Wildcard <- 100

  all_results <- all_results %>%
    dtplyr::lazy_dt() %>%
    dplyr::group_by(.data$SimNo) %>%
    dplyr::mutate(Rank = rank(-.data$Points, ties.method = "random")) %>%
    dplyr::ungroup() %>%
    dplyr::group_by(.data$SimNo, .data$Conference) %>%
    dplyr::mutate(ConfRank = rank(.data$Rank)) %>%
    dplyr::ungroup() %>%
    dplyr::group_by(.data$SimNo, .data$Division) %>%
    dplyr::mutate(DivRank = rank(.data$ConfRank)) %>%
    dplyr::ungroup() %>%
    dplyr::mutate(Playoffs = ifelse(.data$DivRank <= 3, 1, 0)) %>%
    dplyr::group_by(.data$SimNo, .data$Conference) %>%
    dplyr::arrange(.data$Playoffs, .data$ConfRank) %>%
    dplyr::mutate(Wildcard = ifelse(.data$Playoffs == 0, dplyr::row_number(), 100)) %>%
    dplyr::ungroup() %>%
    dplyr::arrange(.data$SimNo, .data$Team) %>%
    dplyr::select(
      .data$SimNo, .data$Team, .data$W, .data$OTW,
      .data$SOW, .data$SOL, .data$OTL, .data$Points,
      .data$Wildcard, .data$Rank, .data$ConfRank,
      .data$DivRank, .data$Playoffs
    ) %>%
    tibble::as_tibble()

  all_results[!is.na(all_results$Wildcard) & all_results$Wildcard <= 2, ]$Playoffs <- 1
  all_results$Wildcard[is.na(all_results$Wildcard)] <- 0
  # all_results$Wildcard<-NULL

  return(all_results)
}


#' Playoff Win Calculator
#'
#' @description wraps PlayoffSeriesOdds with odds generator for a given home and away team
#' @param home_team Home Ice Advantage Team
#' @param away_team Opponent Team
#' @param home_wins Home Ice Advantage Team Wins in Series
#' @param away_wins Opponent Team Wins in Series
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return Odds from 0-1 of home team winning. Away odds are 1 - return value
#' @export
playoffWin <- function(home_team, away_team, home_wins = 0, away_wins = 0, params = NULL) {
  params <- parse_dc_params(params)
  home_odds <- DCPredict(home = home_team, away = away_team, draws = FALSE, params = params)[1]
  away_odds <- 1 - DCPredict(home = away_team, away = home_team, draws = FALSE, params = params)[1]
  return(playoffSeriesOdds(home_odds = home_odds, away_odds = away_odds, home_win = home_wins, away_win = away_wins))
}


#' Random Series Winner
#'
#' @description generate a random series winner given a home and away team
#'
#' @param home_team Home Team name (required)
#' @param away_team Away Team name (Required)
#' @param home_wins Number of home wins (default 0)
#' @param away_wins Number of away team wins (default 0)
#' @param homeAwayOdds pre-calculated home & away team parings odds of a home win. Overrides playoffwin calculation
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return TRUE if the home team wins, else FALSE
#' @export
randomSeriesWinner <- function(home_team, away_team, home_wins = 0, away_wins = 0, homeAwayOdds = NULL, params = NULL) {
  if (is.null(homeAwayOdds)) {
    params <- parse_dc_params(params)
    return(ifelse(stats::runif(1) < playoffWin(home_team = home_team, away_team = away_team, home_wins = home_wins, away_wins = away_wins, params = params),
      home_team, away_team
    ))
  } else {
    hao <- homeAwayOdds[homeAwayOdds$HomeTeam == home_team & homeAwayOdds$AwayTeam == away_team, ]
    if (nrow(hao) == 1) {
      return(ifelse(stats::runif(1) < hao$HomeOdds, home_team, away_team))
    } else {
      # Calculated odds aren't in there, get it manually
      params <- parse_dc_params(params)
      return(ifelse(stats::runif(1) < playoffWin(home_team = home_team, away_team = away_team, home_wins = home_wins, away_wins = away_wins, params = params),
        home_team, away_team
      ))
    }
  }
}


#' Statistical Playoff Series Odds Solver
#'
#' @description Given home and away win odds, produce the odds of the 'home advantage' team winning the series. From \url{http://www.stat.umn.edu/geyer/playoff.html}, modified to function with odds determination.
#' @references \url{http://www.stat.umn.edu/geyer/playoff.html}
#'
#' @param home_odds Team odds with home-ice advantage at home
#' @param away_odds Team odds with home-ice advantage at away (on the road)
#' @param home_win Number of home ice advantage team wins thus far in the series. Default to 0 (prediction before series start)
#' @param away_win Number of away team wins thus far in the series
#' @param ngames Number of games in the series, defaults to 7
#' @param game_home vector of T/F for 'home team' home games. Defaults for NHL best of 7 series: \code{c(T,T,F,F,T,F,T)}
#' @param predict_games_to_win (Defualt False) If TRUE, returns the table of ways the series could finish.
#'
#' @return numeric odds of home team win series (1-odds for away odds)
#' @export
playoffSeriesOdds <- function(home_odds, away_odds, home_win = 0, away_win = 0, ngames = NULL, game_home = NULL, predict_games_to_win = FALSE) {
  if (is.null(ngames)) {
    ngames <- 7
  }
  if (is.null(game_home) & ngames == 7) {
    game_home <- c(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, TRUE)
  } else {
    game_home <- rep(c(TRUE, FALSE), as.integer(ngames + 1 / 2))[1:ngames]
  }

  game_to <- ceiling(ngames / 2)

  if (length(home_odds) > 1 | length(away_odds) > 1) {
    stop("handle only one series at a time")
  }
  p1_home <- home_odds
  p1_road <- away_odds

  if (p1_home < 0 | p1_home > 1 | p1_road < 0 | p1_road > 1) {
    stop("impossible odds")
  }

  home_win <- as.integer(home_win)
  away_win <- as.integer(away_win)

  if (home_win < 0 | away_win < 0) {
    stop("negative number of wins impossible")
  }
  if (home_win >= game_to) {
    message("series already won")
    return(1)
  }
  if (away_win >= game_to) {
    message("series already won")
    return(0)
  }

  games_played <- home_win + away_win

  if (games_played > ngames) {
    stop("total wins greater than games impossible")
  }
  if (games_played == ngames) {
    stop("nothing to do, series over")
  }

  x.g <- games_played
  x.w1 <- home_win
  x.w2 <- away_win
  x.p <- 1.0
  finished_series <- NULL
  for (i in (games_played + 1):ngames) {
    p1now <- ifelse(game_home[i], p1_home, p1_road)
    l <- length(x.g)
    y.w1 <- c(x.w1 + 1, x.w1[l])
    y.w2 <- c(x.w2[1], x.w2 + 1)
    y.g <- c(x.g + 1, x.g[1] + 1)
    y.p <- c(x.p * p1now, 0)
    y.p <- y.p + c(0, x.p * (1 - p1now))
    unfinished_series <- y.w1 < game_to & y.w2 < game_to
    if (any(!unfinished_series)) {
      series <- cbind(y.g, y.w1, y.w2, y.p)
      finished_series <- rbind(finished_series, series[!unfinished_series, ])
    }
    x.g <- y.g[unfinished_series]
    x.w1 <- y.w1[unfinished_series]
    x.w2 <- y.w2[unfinished_series]
    x.p <- y.p[unfinished_series]
  }

  if (predict_games_to_win == FALSE) {
    p1total <- sum(finished_series[finished_series[, 2] == game_to, 4])

    return(p1total)
  } else {
    return(unfinished_series)
  }
}


#' simulate Playoffs
#'
#' @description Solves playoff odds by MC simulation.
#'
#' @param summary_results summary results
#' @param nsims Number of playoff sims to run. Too many takes a long time.
#' @param cores Number of processor cores to use
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a data frame of each teams' odds of winning each round (First Round, Second Round, Conference Finals and Stanley Cup)
#' @export
simulatePlayoffs <- function(summary_results = NULL, nsims = 1e5, cores = NULL, params = NULL) {
  params <- parse_dc_params(params)
  cores <- parseCores(cores)
  # TODO use compile_predictions for this?
  if (is.null(summary_results)) {
    filelist <- list.files(path = getOption("HockeyModel.prediction.path"))
    pdates <- substr(filelist, 1, 10) # gets the dates list of prediction
    pdates <- pdates[pdates != "graphics"]
    lastp <- as.Date(max(pdates))
    summary_results <- readRDS(file.path(getOption("HockeyModel.prediction.path"), paste0(lastp, "-predictions.RDS")))
  }

  summary_results <- summary_results %>%
    dplyr::mutate(
      "Conf" = getTeamConferences(.data$Team),
      "Div" = getTeamDivisions(.data$Team)
    )
  if ("p_rank3" %in% names(summary_results)) {
    # Shortcut for having prank3, 4, 5, 6 instead of prank34, and prank56. add them
    summary_results <- summary_results %>%
      dplyr::mutate(
        "p_rank_34" = .data$p_rank3 + .data$p_rank4,
        "p_rank_56" = .data$p_rank5 + .data$p_rank6
      )
  }
  east_results <- summary_results %>% dplyr::filter(.data$Conf == "Eastern")
  west_results <- summary_results %>% dplyr::filter(.data$Conf == "Western")

  homeAwayOdds <- getAllHomeAwayOdds(summary_results$Team, params = params)

  simresults <- data.frame(
    "SimNo" = integer(),
    "l1" = character(),
    "l2" = character(),
    "l3" = character(),
    "l4" = character(),
    "l5" = character(),
    "l6" = character(),
    "l7" = character(),
    "l8" = character(),
    "series1" = character(),
    "series2" = character(),
    "series3" = character(),
    "series4" = character(),
    "series5" = character(),
    "series6" = character(),
    "series7" = character(),
    "series8" = character(),
    "series9" = character(),
    "series10" = character(),
    "series11" = character(),
    "series12" = character(),
    "series13" = character(),
    "series14" = character(),
    "series15" = character()
  )

  # currentSeries<-getAPISeries()

  currentSeries <- data.frame(
    "Round" = integer(), "Series" = integer(), "HomeTeam" = character(), "AwayTeam" = character(),
    "HomeWins" = integer(), "AwayWins" = integer(), "HomeSeed" = integer(), "AwaySeed" = integer(),
    "Statsu" = character(), "SeriesID" = integer()
  )

  if (nrow(currentSeries) == 0) {
    message("too early to mix in real-life series")
    completedSeries <- data.frame("Series" = character(), "Winner" = character(), "Loser" = character())
    currentSeries <- data.frame(
      "Round" = integer(), "Series" = integer(), "HomeTeam" = character(), "AwayTeam" = character(),
      "HomeWins" = integer(), "AwayWins" = integer(), "HomeSeed" = integer(), "AwaySeed" = integer(),
      "Statsu" = character(), "SeriesID" = integer()
    )
  } else {
    completedSeries <- getCompletedSeries(currentSeries)
    for (s in currentSeries[currentSeries$Status == "Ongoing", ]$SeriesID) {
      homeAwayOdds[homeAwayOdds$HomeTeam == currentSeries[currentSeries$SeriesID == s, ]$HomeTeam &
        homeAwayOdds$AwayTeam == currentSeries[currentSeries$SeriesID == s, ]$AwayTeam, ]$HomeOdds <-
        playoffWin(
          home_team = currentSeries[currentSeries$SeriesID == s, ]$HomeTeam,
          away_team = currentSeries[currentSeries$SeriesID == s, ]$AwayTeam,
          home_wins = currentSeries[currentSeries$SeriesID == s, ]$HomeWins,
          away_wins = currentSeries[currentSeries$SeriesID == s, ]$AwayWins,
          params = params
        )
    }
  }

  if (cores > 1) {
    cl <- parallel::makeCluster(cores)
    doSNOW::registerDoSNOW(cl)


    `%dopar%` <- foreach::`%dopar%`

    simresults <- foreach::foreach(i = 1:(cores * 100), .combine = "rbind", .packages = "HockeyModel") %dopar% {
      simresults <- playoffSolverEngine(nsims = ceiling(nsims / (cores * 100)), completedSeries = completedSeries, east_results = east_results, west_results = west_results, currentSeries = currentSeries, summary_results = summary_results, homeAwayOdds = homeAwayOdds)
      return(simresults)
    }

    parallel::stopCluster(cl)
    gc(verbose = FALSE)
  } else {
    # Single cores is easier for testing
    simresults <- playoffSolverEngine(nsims = nsims, completedSeries = completedSeries, east_results = east_results, west_results = west_results, currentSeries = currentSeries, summary_results = summary_results, homeAwayOdds = homeAwayOdds)
  }

  simodds <- data.frame("Team" = summary_results$Team)

  simodds <- simodds %>%
    dplyr::rowwise() %>%
    dplyr::mutate(
      "Make_Playoffs" = summary_results[summary_results$Team == .data$Team, ]$Playoffs,
      "Win_First_Round" = (nrow(simresults[simresults$series1 == .data$Team, ]) +
        nrow(simresults[simresults$series2 == .data$Team, ]) +
        nrow(simresults[simresults$series3 == .data$Team, ]) +
        nrow(simresults[simresults$series4 == .data$Team, ]) +
        nrow(simresults[simresults$series5 == .data$Team, ]) +
        nrow(simresults[simresults$series6 == .data$Team, ]) +
        nrow(simresults[simresults$series7 == .data$Team, ]) +
        nrow(simresults[simresults$series8 == .data$Team, ])) / nrow(simresults),
      "Win_Second_Round" = (nrow(simresults[simresults$series9 == .data$Team, ]) +
        nrow(simresults[simresults$series10 == .data$Team, ]) +
        nrow(simresults[simresults$series11 == .data$Team, ]) +
        nrow(simresults[simresults$series12 == .data$Team, ])) / nrow(simresults),
      "Win_Conference" = (nrow(simresults[simresults$series13 == .data$Team, ]) +
        nrow(simresults[simresults$series14 == .data$Team, ])) / nrow(simresults),
      "Win_Cup" = nrow(simresults[simresults$series15 == .data$Team, ]) / nrow(simresults)
    ) %>%
    dplyr::arrange(dplyr::desc(.data$Win_Cup), dplyr::desc(.data$Win_Conference), dplyr::desc(.data$Win_Second_Round), dplyr::desc(.data$Win_First_Round), dplyr::desc(.data$Make_Playoffs), .data$Team) %>%
    as.data.frame()

  return(simodds)
}

reseedTwoTeams <- function(team1, team2, summary_results, p1 = NULL) {
  t1p <- summary_results[summary_results$Team == team1, ]$meanPoints
  t2p <- summary_results[summary_results$Team == team2, ]$meanPoints

  if (!is.null(p1) && !is.null(nrow(p1)) && nrow(p1) > 0) {
    if (team1 == p1) {
      return(c(team1, team2))
    } else if (team2 == p1) {
      return(c(team2, team1))
    }
  }
  if (t1p > t2p) {
    return(c(team1, team2))
  } else if (t2p > t1p) {
    return(c(team2, team1))
  } else {
    if (stats::runif(1) < 0.5) {
      return(c(team1, team2))
    } else {
      return(c(team2, team1))
    }
  }
}

#' Given current seriess and a series number and home and away teams, either return true series winner, random series winner (with home/away wins considered) or random series winner
#'
#' @param series_number Series number from 1:15
#' @param currentSeries the full list of series returned from getAPISeries
#' @param homeTeam Home Team extracted from summary_results
#' @param awayTeam away Team extracted from summary_results
#' @param homeAwayOdds if calculated, the odds of a home or away team win
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return a series winner (team name)
single_series_solver <- function(series_number, currentSeries, homeTeam, awayTeam, homeAwayOdds = NULL, params = NULL) {
  params <- parse_dc_params(params)
  if (is.na(currentSeries) || nrow(currentSeries) == 0) {
    return(randomSeriesWinner(homeTeam, awayTeam, homeAwayOdds = homeAwayOdds, params = params))
  }
  series <- currentSeries[currentSeries$SeriesID == series_number, ]
  if (nrow(series[series$Status == "Complete", ]) == 1) {
    if (series$HomeTeam != homeTeam | series$AwayTeam != awayTeam) {
      warning("Team Mismatch series ", series_number, ". Home Team expected ", series$HomeTeam, " got ", homeTeam, ". Away Team expected ", series$AwayTeam, " got ", awayTeam, ". Using API series information.")
    }
    return(ifelse(series$HomeWins > series$AwayWins, series$HomeTeam, series$AwayTeam))
  } else if (nrow(currentSeries[currentSeries == series_number & currentSeries$Status == "Ongoing", ]) == 1) {
    if (series$HomeTeam != homeTeam | series$AwayTeam != awayTeam) {
      warning("Team Mismatch series ", series_number, ". Home Team expected ", series$HomeTeam, " got ", homeTeam, ". Away Team expected ", series$AwayTeam, " got ", awayTeam, ". Using API series information.")
    }
    return(randomSeriesWinner(series$HomeTeam, series$AwayTeam, home_wins = series$HomeWins, away_wins = series$AwayWins, homeAwayOdds = homeAwayOdds, params = params))
  } else {
    return(randomSeriesWinner(homeTeam, awayTeam, homeAwayOdds = homeAwayOdds, params = params))
  }
}

getCompletedSeries <- function(currentSeries) {
  completedSeries <- currentSeries %>%
    dplyr::filter(.data$Status == "Complete") %>%
    dplyr::mutate(
      "Winner" = dplyr::case_when(
        .data$HomeWins > .data$AwayWins ~ .data$HomeTeam,
        .data$HomeWins < .data$AwayWins ~ .data$AwayTeam
      ),
      "Loser" = dplyr::case_when(
        .data$HomeWins > .data$AwayWins ~ .data$AwayTeam,
        .data$HomeWins < .data$AwayWins ~ .data$HomeTeam
      ),
      "Series" = paste0("series", .data$SeriesID)
    ) %>%
    dplyr::select(c("Series", "Winner", "Loser"))
  return(completedSeries)
}

#' Playoff Solver Engine
#'
#' @description Does the actual simulating. A function so it's parallelizable. Not to be called directly. Exported for parallel's use
#' @param nsims number of sims (in each core)
#' @param completedSeries completed series
#' @param east_results east_results
#' @param west_results west_results
#' @param currentSeries currentSeries
#' @param summary_results summary_results
#' @param homeAwayOdds precalculated home & away pairs of odds - if available.
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @export
playoffSolverEngine <- function(nsims, completedSeries, east_results, west_results, currentSeries, summary_results, homeAwayOdds, params = NULL) {
  params <- parse_dc_params(params)
  simresults <- data.frame(
    "SimNo" = integer(),
    "l1" = character(),
    "l2" = character(),
    "l3" = character(),
    "l4" = character(),
    "l5" = character(),
    "l6" = character(),
    "l7" = character(),
    "l8" = character(),
    "series1" = character(),
    "series2" = character(),
    "series3" = character(),
    "series4" = character(),
    "series5" = character(),
    "series6" = character(),
    "series7" = character(),
    "series8" = character(),
    "series9" = character(),
    "series10" = character(),
    "series11" = character(),
    "series12" = character(),
    "series13" = character(),
    "series14" = character(),
    "series15" = character()
  )
  srvec <- c()

  east_results <- as.data.frame(east_results)
  west_results <- as.data.frame(west_results)

  for (sim in 1:nsims) {
    if (all(paste0("series", 1:8) %in% completedSeries$Series)) {
      series1 <- completedSeries[completedSeries$Series == "series1", ]$Winner
      series2 <- completedSeries[completedSeries$Series == "series2", ]$Winner
      series3 <- completedSeries[completedSeries$Series == "series3", ]$Winner
      series4 <- completedSeries[completedSeries$Series == "series4", ]$Winner
      series5 <- completedSeries[completedSeries$Series == "series5", ]$Winner
      series6 <- completedSeries[completedSeries$Series == "series6", ]$Winner
      series7 <- completedSeries[completedSeries$Series == "series7", ]$Winner
      series8 <- completedSeries[completedSeries$Series == "series8", ]$Winner
      l1 <- completedSeries[completedSeries$Series == "series1", ]$Loser
      l2 <- completedSeries[completedSeries$Series == "series2", ]$Loser
      l3 <- completedSeries[completedSeries$Series == "series3", ]$Loser
      l4 <- completedSeries[completedSeries$Series == "series4", ]$Loser
      l5 <- completedSeries[completedSeries$Series == "series5", ]$Loser
      l6 <- completedSeries[completedSeries$Series == "series6", ]$Loser
      l7 <- completedSeries[completedSeries$Series == "series7", ]$Loser
      l8 <- completedSeries[completedSeries$Series == "series8", ]$Loser
    } else {
      # solve east conference
      er <- east_results
      eastseries <- list() # p1 to p8 (conference winner, div #2, 2nd place, second div#2, second div#3, wc1, div #3, wc2)
      serieslist <- list()
      for (s in 1:4) {
        if (paste0("series", s) %in% completedSeries$series) {
          eastseries[paste0("p", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Winner
          eastseries[paste0("p", 9 - s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Loser
          serieslist[paste0("series", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Winner
          serieslist[paste0("l", s)] <- completedSeries[completedSeries$Series == paste0("series", s), ]$Loser
          er <- er[er$Team != completedSeries[completedSeries$Series == paste0("series", s), ]$Winner, ]
          er <- er[er$Team != completedSeries[completedSeries$Series == paste0("series", s), ]$Loser, ]
        } else if (s %in% currentSeries$SeriesID) {
          eastseries[paste0("p", s)] <- currentSeries[currentSeries$SeriesID == s, ]$HomeTeam
          eastseries[paste0("p", 9 - s)] <- currentSeries[currentSeries$SeriesID == s, ]$AwayTeam
          er <- er[er$Team != currentSeries[currentSeries$SeriesID == s, ]$HomeTeam, ]
          er <- er[er$Team != currentSeries[currentSeries$SeriesID == s, ]$AwayTeam, ]
        }
      }
      if (!("p1" %in% names(eastseries))) {
        eastseries["p1"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank1), ]$Team
        er <- er[er$Team != eastseries["p1"], ]
        p1div <- getTeamDivisions(eastseries["p1"])
      } else {
        p1div <- getTeamDivisions(eastseries["p1"])
      }
      if (!("p2" %in% names(eastseries))) {
        eastseries["p2"] <- er[er$Div == p1div, ]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_34)]
        er <- er[er$Team != eastseries["p2"], ]
        eastseries["p7"] <- er[er$Div == p1div, ]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_56)]
        er <- er[er$Team != eastseries["p7"], ]
      }
      if (!("p3" %in% names(eastseries))) {
        eastseries["p3"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank2)]
        er <- er[er$Team != eastseries["p3"], ]
      }
      if (!("p4" %in% names(eastseries))) {
        eastseries["p4"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_34)]
        er <- er[er$Team != eastseries["p4"], ]
        eastseries["p5"] <- er[er$Div != p1div, ]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_56)]
        er <- er[er$Team != eastseries["p5"], ]
      }

      if (!("p6" %in% names(eastseries))) {
        eastseries["p6"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank7), ]$Team
        er <- er[er$Team != eastseries["p6"], ]
      }

      if (!("p8" %in% names(eastseries))) {
        eastseries["p8"] <- er[sample(1:nrow(er), size = 1, prob = er$p_rank8), ]$Team
        # er<-er[er$Team != eastseries['p8'],]
      }

      if (!("series1" %in% serieslist)) {
        series1 <- single_series_solver(series_number = 1, currentSeries = currentSeries, homeTeam = eastseries[["p1"]], awayTeam = eastseries[["p8"]], homeAwayOdds = homeAwayOdds)
        l1 <- ifelse(series1 == eastseries[["p1"]], eastseries[["p8"]], eastseries[["p1"]])
      } else {
        series1 <- serieslist[["series1"]]
        l1 <- serieslist[["l1"]]
      }
      if (!("series2" %in% serieslist)) {
        series2 <- single_series_solver(series_number = 2, currentSeries = currentSeries, homeTeam = eastseries[["p2"]], awayTeam = eastseries[["p7"]], homeAwayOdds = homeAwayOdds)
        l2 <- ifelse(series2 == eastseries[["p2"]], eastseries[["p7"]], eastseries[["p2"]])
      } else {
        series2 <- serieslist[["series2"]]
        l2 <- serieslist[["l2"]]
      }
      if (!("series3" %in% serieslist)) {
        series3 <- single_series_solver(series_number = 3, currentSeries = currentSeries, homeTeam = eastseries[["p3"]], awayTeam = eastseries[["p6"]], homeAwayOdds = homeAwayOdds)
        l3 <- ifelse(series3 == eastseries[["p3"]], eastseries[["p6"]], eastseries[["p3"]])
      } else {
        series3 <- serieslist[["series3"]]
        l3 <- serieslist[["l3"]]
      }
      if (!("series4" %in% serieslist)) {
        series4 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = eastseries[["p4"]], awayTeam = eastseries[["p5"]], homeAwayOdds = homeAwayOdds)
        l4 <- ifelse(series4 == eastseries[["p4"]], eastseries[["p5"]], eastseries[["p4"]])
      } else {
        series4 <- serieslist[["series4"]]
        l4 <- serieslist[["l4"]]
      }
      rm(er, serieslist, eastseries)

      wr <- west_results
      westseries <- list() # p1 to p8 (conference winner, div #2, 2nd place, second div#2, second div#3, wc1, div #3, wc2)
      serieslist <- list()
      for (s in 1:4) {
        # of course, west series first rounds are # 5-8, so s+4 for all references
        if (paste0("series", s) %in% completedSeries$series) {
          westseries[paste0("p", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner
          westseries[paste0("p", 9 - s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser
          serieslist[paste0("series", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner
          serieslist[paste0("l", s)] <- completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser
          wr <- wr[wr$Team != completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Winner, ]
          wr <- wr[wr$Team != completedSeries[completedSeries$Series == paste0("series", s + 4), ]$Loser, ]
        } else if (s %in% currentSeries$SeriesID) {
          westseries[paste0("p", s)] <- currentSeries[currentSeries$SeriesID == s + 4, ]$HomeTeam
          westseries[paste0("p", 9 - s)] <- currentSeries[currentSeries$SeriesID == s + 4, ]$AwayTeam
          wr <- wr[wr$Team != currentSeries[currentSeries$SeriesID == s + 4, ]$HomeTeam, ]
          wr <- wr[wr$Team != currentSeries[currentSeries$SeriesID == s + 4, ]$AwayTeam, ]
        }
      }
      if (!("p1" %in% names(westseries))) {
        westseries["p1"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank1), ]$Team
        wr <- wr[wr$Team != westseries["p1"], ]
        p1div <- getTeamDivisions(westseries["p1"])
      } else {
        p1div <- getTeamDivisions(westseries["p1"])
      }
      if (!("p2" %in% names(westseries))) {
        westseries["p2"] <- wr[wr$Div == p1div, ]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_34)]
        wr <- wr[wr$Team != westseries["p2"], ]
        westseries["p7"] <- wr[wr$Div == p1div, ]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_56)]
        wr <- wr[wr$Team != westseries["p7"], ]
      }
      if (!("p3" %in% names(westseries))) {
        westseries["p3"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank2)]
        wr <- wr[wr$Team != westseries["p3"], ]
      }
      if (!("p4" %in% names(westseries))) {
        westseries["p4"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_34)]
        wr <- wr[wr$Team != westseries["p4"], ]
        westseries["p5"] <- wr[wr$Div != p1div, ]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_56)]
        wr <- wr[wr$Team != westseries["p5"], ]
      }

      if (!("p6" %in% names(westseries))) {
        westseries["p6"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank7), ]$Team
        wr <- wr[wr$Team != westseries["p6"], ]
      }

      if (!("p8" %in% names(westseries))) {
        westseries["p8"] <- wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank8), ]$Team
        # wr<-wr[wr$Team != westseries['p8'],]
      }

      if (!("series1" %in% serieslist)) {
        series5 <- single_series_solver(series_number = 5, currentSeries = currentSeries, homeTeam = westseries[["p1"]], awayTeam = westseries[["p8"]], homeAwayOdds = homeAwayOdds)
        l5 <- ifelse(series5 == westseries[["p1"]], westseries[["p8"]], westseries[["p1"]])
      } else {
        series5 <- serieslist[["series1"]]
        l5 <- serieslist[["l1"]]
      }
      if (!("series2" %in% serieslist)) {
        series6 <- single_series_solver(series_number = 6, currentSeries = currentSeries, homeTeam = westseries[["p2"]], awayTeam = westseries[["p7"]], homeAwayOdds = homeAwayOdds)
        l6 <- ifelse(series6 == westseries[["p2"]], westseries[["p7"]], westseries[["p2"]])
      } else {
        series6 <- serieslist[["series2"]]
        l6 <- serieslist[["l2"]]
      }
      if (!("series3" %in% serieslist)) {
        series7 <- single_series_solver(series_number = 7, currentSeries = currentSeries, homeTeam = westseries[["p3"]], awayTeam = westseries[["p6"]], homeAwayOdds = homeAwayOdds)
        l7 <- ifelse(series7 == westseries[["p3"]], westseries[["p6"]], westseries[["p3"]])
      } else {
        series7 <- serieslist[["series3"]]
        l7 <- serieslist[["l3"]]
      }
      if (!("series4" %in% serieslist)) {
        series8 <- single_series_solver(series_number = 8, currentSeries = currentSeries, homeTeam = westseries[["p4"]], awayTeam = westseries[["p5"]], homeAwayOdds = homeAwayOdds)
        l8 <- ifelse(series8 == westseries[["p4"]], westseries[["p5"]], westseries[["p4"]])
      } else {
        series8 <- serieslist[["series4"]]
        l8 <- serieslist[["l4"]]
      }
      rm(wr, serieslist, westseries)


      # if('series1' %in% completedSeries$Series){
      #   series1 <- completedSeries[completedSeries$Series == 'series1', ]$Winner
      #   l1 <- completedSeries[completedSeries$Series == 'series1', ]$Loser
      #   er<-er[er$Team != series1,]
      #   er<-er[er$Team != l1,]
      # } else if (1 %in% currentSeries$SeriesID){
      #   e1.1 <- currentSeries[currentSeries$SeriesID == 1, ]$HomeTeam
      #   er<-er[er$Team != e1.1,]
      #   ewc2 <- currentSeries[currentSeries$SeriesID == 1, ]$AwayTeam
      #   er<-er[er$Team != ewc2,]
      #   odds<-playoffWin(e1.1, ewc2, currentSeries[currentSeries$SeriesID == 1, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 1, ]$AwayWins, params = params)
      #   series1<-sample(c(e1.1, ewc2), size = 1, prob = c(odds, 1-odds))
      #   l1 <- ifelse(series4 == e1.1, ewc2, e1.1) #If winner = a, then b, else a
      # } else {
      #   e1.1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank1),]$Team
      #   er<-er[er$Team != e1.1,]
      #   ewc2<-er[sample(1:nrow(er), size = 1, prob = er$p_rank8),]$Team
      #   er<-er[er$Team != ewc2,]
      #   series1 <- single_series_solver(series_number = 1, currentSeries = currentSeries, homeTeam = e1.1, awayTeam = ewc2, homeAwayOdds = homeAwayOdds)
      #   l1 <- ifelse(series1 == e1.1, ewc2, e1.1) #If winner = a, then b, else a
      # }
      # p1div<-getTeamDivisions(e1.1)
      #
      # if('series2' %in% completedSeries$Series){
      #   series2 <- completedSeries[completedSeries$Series == 'series2', ]$Winner
      #   l2 <- completedSeries[completedSeries$Series == 'series2', ]$Loser
      #   er<-er[er$Team != series2,]
      #   er<-er[er$Team != l2,]
      # } else if (2 %in% currentSeries$SeriesID){
      #   e1.2 <- currentSeries[currentSeries$SeriesID == 2, ]$HomeTeam
      #   er<-er[er$Team != e1.1,]
      #   e1.3 <- currentSeries[currentSeries$SeriesID == 2, ]$AwayTeam
      #   er<-er[er$Team != e1.3,]
      #   odds<-playoffWin(e1.2, e1.3, currentSeries[currentSeries$SeriesID == 2, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 2, ]$AwayWins, params = params)
      #   series2<-sample(c(e1.2, e1.3), size = 1, prob = c(odds, 1-odds))
      #   l2 <- ifelse(series2 == e1.2, e1.3, e1.2) #If winner = a, then b, else a
      # } else {
      #   e1.2<-er[er$Div == p1div,]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_34)]
      #   er<-er[er$Team != e1.2,]
      #   e1.3<-er[er$Div == p1div,]$Team[sample(1:nrow(er[er$Div == p1div, ]), size = 1, prob = er[er$Div == p1div, ]$p_rank_56)]
      #   er<-er[er$Team != e1.3,]
      #   series2 <- single_series_solver(series_number = 2, currentSeries = currentSeries, homeTeam = e1.2, awayTeam = e1.3, homeAwayOdds = homeAwayOdds)
      #   l2 <- ifelse(series2 == e1.2, e1.3, e1.2) #If winner = a, then b, else a
      # }
      #
      # if('series3' %in% completedSeries$Series){
      #   series3 <- completedSeries[completedSeries$Series == 'series3', ]$Winner
      #   l3 <- completedSeries[completedSeries$Series == 'series3', ]$Loser
      #   er<-er[er$Team != series3,]
      #   er<-er[er$Team != l3,]
      # } else if (3 %in% currentSeries$SeriesID){
      #   e2.1 <- currentSeries[currentSeries$SeriesID == 3, ]$HomeTeam
      #   er<-er[er$Team != e2.1,]
      #   ewc1 <- currentSeries[currentSeries$SeriesID == 3, ]$AwayTeam
      #   er<-er[er$Team != ewc1,]
      #   odds<-playoffWin(e2.1, ewc1, currentSeries[currentSeries$SeriesID == 3, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 3, ]$AwayWins, params = params)
      #   series3<-sample(c(e2.1, ewc1), size = 1, prob = c(odds, 1-odds))
      #   l3 <- ifelse(series3 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
      # } else {
      #   e2.1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank2),]$Team
      #   er<-er[er$Team != e2.1,]
      #   ewc1<-er[sample(1:nrow(er), size = 1, prob = er$p_rank7),]$Team
      #   er<-er[er$Team != ewc1,]
      #   series3 <- single_series_solver(series_number = 3, currentSeries = currentSeries, homeTeam = e2.1, awayTeam = ewc1, homeAwayOdds = homeAwayOdds)
      #   l3 <- ifelse(series3 == e2.1, ewc1, e2.1) #If winner = a, then b, else a
      # }
      #
      # if('series4' %in% completedSeries$Series){
      #   series4 <- completedSeries[completedSeries$Series == 'series4', ]$Winner
      #   l4 <- completedSeries[completedSeries$Series == 'series4', ]$Loser
      #   er<-er[er$Team != series4,]
      #   er<-er[er$Team != l4,]
      # } else if (4 %in% currentSeries$SeriesID){
      #   e2.2 <- currentSeries[currentSeries$SeriesID == 4, ]$HomeTeam
      #   er<-er[er$Team != e2.2,]
      #   e2.3 <- currentSeries[currentSeries$SeriesID == 4, ]$AwayTeam
      #   er<-er[er$Team != e2.3,]
      #   odds<-playoffWin(e2.2, e2.3, currentSeries[currentSeries$SeriesID == 4, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 4, ]$AwayWins, params = params)
      #   series4<-sample(c(e2.2, e2.3), size = 1, prob = c(odds, 1-odds))
      #   l4 <- ifelse(series4 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
      # } else {
      #   e2.2<-er[er$Div != p1div,]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_34)]
      #   er<-er[er$Team != e2.2,]
      #   e2.3<-er[er$Div != p1div,]$Team[sample(1:nrow(er[er$Div != p1div, ]), size = 1, prob = er[er$Div != p1div, ]$p_rank_56)]
      #   er<-er[er$Team != e2.3,]
      #   series4 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = e2.2, awayTeam = e2.3, homeAwayOdds = homeAwayOdds)
      #   l4 <- ifelse(series4 == e2.2, e2.3, e2.2) #If winner = a, then b, else a
      # }
      #
      # wr<-west_results
      #
      # if('series5' %in% completedSeries$Series){
      #   series5 <- completedSeries[completedSeries$Series == 'series5', ]$Winner
      #   l5 <- completedSeries[completedSeries$Series == 'series5', ]$Loser
      #   wr<-wr[wr$Team != series5,]
      #   wr<-wr[wr$Team != l5,]
      # } else if (5 %in% currentSeries$SeriesID){
      #   w1.1 <- currentSeries[currentSeries$SeriesID == 5, ]$HomeTeam
      #   wr<-wr[wr$Team != w1.1,]
      #   wwc2 <- currentSeries[currentSeries$SeriesID == 5, ]$AwayTeam
      #   wr<-wr[wr$Team != wwc2,]
      #   odds<-playoffWin(w1.1, wwc2, currentSeries[currentSeries$SeriesID == 5, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 5, ]$AwayWins, params = params)
      #   series5<-sample(c(w1.1, wwc2), size = 1, prob = c(odds, 1-odds))
      #   l5 <- ifelse(series5 == w1.1, wwc2, w1.1) #If winner = a, then b, else a
      # } else {
      #   w1.1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank1),]$Team
      #   wr<-wr[wr$Team != w1.1,]
      #   wwc2<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank8),]$Team
      #   wr<-wr[wr$Team != wwc2,]
      #   series5 <- single_series_solver(series_number = 5, currentSeries = currentSeries, homeTeam = w1.1, awayTeam = wwc2, homeAwayOdds = homeAwayOdds)
      #   l5 <- ifelse(series5 == w1.1, wwc2, w1.1) #If winner = a, then b, else a
      # }
      # p1div<-getTeamDivisions(w1.1)
      #
      # if('series6' %in% completedSeries$Series){
      #   series6 <- completedSeries[completedSeries$Series == 'series6', ]$Winner
      #   l6 <- completedSeries[completedSeries$Series == 'series6', ]$Loser
      #   wr<-wr[wr$Team != series6,]
      #   wr<-wr[wr$Team != l6,]
      # } else if (6 %in% currentSeries$SeriesID) {
      #   w1.2 <- currentSeries[currentSeries$SeriesID == 6, ]$HomeTeam
      #   wr<-wr[wr$Team != w1.1,]
      #   w1.3 <- currentSeries[currentSeries$SeriesID == 6, ]$AwayTeam
      #   wr<-wr[wr$Team != w1.3,]
      #   odds<-playoffWin(w1.2, w1.3, currentSeries[currentSeries$SeriesID == 6, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 6, ]$AwayWins, params = params)
      #   series6<-sample(c(w1.2, w1.3), size = 1, prob = c(odds, 1-odds))
      #   l6 <- ifelse(series6 == w1.2, w1.3, w1.2) #If winner = a, then b, else a
      # } else {
      #   w1.2<-wr[wr$Div == p1div,]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_34)]
      #   wr<-wr[wr$Team != w1.2,]
      #   w1.3<-wr[wr$Div == p1div,]$Team[sample(1:nrow(wr[wr$Div == p1div, ]), size = 1, prob = wr[wr$Div == p1div, ]$p_rank_56)]
      #   wr<-wr[wr$Team != w1.3,]
      #   series6 <- single_series_solver(series_number = 6, currentSeries = currentSeries, homeTeam = w1.2, awayTeam = w1.3, homeAwayOdds = homeAwayOdds)
      #   l6 <- ifelse(series6 == w1.2, w1.3, w1.2) #If winner = a, then b, else a
      # }
      #
      # if('series7' %in% completedSeries$Series){
      #   series7 <- completedSeries[completedSeries$Series == 'series7', ]$Winner
      #   l7 <- completedSeries[completedSeries$Series == 'series7', ]$Loser
      #   wr<-wr[wr$Team != series7,]
      #   wr<-wr[wr$Team != l7,]
      # } else if (7 %in% currentSeries$SeriesID){
      #   w2.1 <- currentSeries[currentSeries$SeriesID == 7, ]$HomeTeam
      #   wr<-wr[wr$Team != w1.1,]
      #   wwc1 <- currentSeries[currentSeries$SeriesID == 7, ]$AwayTeam
      #   wr<-wr[wr$Team != wwc1,]
      #   odds<-playoffWin(w2.1, wwc1, currentSeries[currentSeries$SeriesID == 7, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 7, ]$AwayWins, params = params)
      #   series7<-sample(c(w2.1, wwc1), size = 1, prob = c(odds, 1-odds))
      #   l7 <- ifelse(series7 == w2.1, wwc1, w2.1) #If winner = a, then b, else a
      # } else {
      #   w2.1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank2),]$Team
      #   wr<-wr[wr$Team != w2.1,]
      #   wwc1<-wr[sample(1:nrow(wr), size = 1, prob = wr$p_rank7),]$Team
      #   wr<-wr[wr$Team != wwc1,]
      #   series7 <- single_series_solver(series_number = 7, currentSeries = currentSeries, homeTeam = w2.1, awayTeam = wwc1, homeAwayOdds = homeAwayOdds)
      #   l7 <- ifelse(series7 == w2.1, wwc1, w2.1) #If winner = a, then b, else a
      # }
      #
      # if('series8' %in% completedSeries$Series){
      #   series8 <- completedSeries[completedSeries$Series == 'series8', ]$Winner
      #   l8 <- completedSeries[completedSeries$Series == 'series8', ]$Loser
      #   wr<-wr[wr$Team != series8,]
      #   wr<-wr[wr$Team != l8,]
      # } else if (8 %in% currentSeries$SeriesID){
      #   w2.2 <- currentSeries[currentSeries$SeriesID == 8, ]$HomeTeam
      #   wr<-wr[wr$Team != w2.2,]
      #   w2.3 <- currentSeries[currentSeries$SeriesID == 8, ]$AwayTeam
      #   wr<-wr[wr$Team != w2.3,]
      #   odds<-playoffWin(w2.2, w2.3, currentSeries[currentSeries$SeriesID == 8, ]$HomeWins,
      #                    currentSeries[currentSeries$SeriesID == 8, ]$AwayWins, params = params)
      #   series8<-sample(c(w2.2, w2.3), size = 1, prob = c(odds, 1-odds))
      #   l8 <- ifelse(series8 == w2.2, w2.3, w2.2) #If winner = a, then b, else a
      # } else {
      #   w2.2<-wr[wr$Div != p1div,]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_34)]
      #   wr<-wr[wr$Team != w2.2,]
      #   w2.3<-wr[wr$Div != p1div,]$Team[sample(1:nrow(wr[wr$Div != p1div, ]), size = 1, prob = wr[wr$Div != p1div, ]$p_rank_56)]
      #   wr<-wr[wr$Team != w2.3,]
      #   series8 <- single_series_solver(series_number = 4, currentSeries = currentSeries, homeTeam = w2.2, awayTeam = w2.3, homeAwayOdds = homeAwayOdds)
      #   l8 <- ifelse(series8 == w2.2, w2.3, w2.2) #If winner = a, then b, else a
      # }
    }

    # No reseeding for round 2 (but in reality yeah there is, wildCard doesn't have home advantage)
    if ("series9" %in% completedSeries$Series) {
      series9 <- completedSeries[completedSeries$Series == "series9", ]$Winner
    } else {
      rs <- reseedTwoTeams(series1, series2, summary_results, currentSeries[currentSeries$SeriesID == 1, ]$HomeTeam)
      series9 <- single_series_solver(series_number = 9, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    if ("series10" %in% completedSeries$Series) {
      series10 <- completedSeries[completedSeries$Series == "series10", ]$Winner
    } else {
      rs <- reseedTwoTeams(series3, series4, summary_results, currentSeries[currentSeries$SeriesID == 3, ]$HomeTeam)
      series10 <- single_series_solver(series_number = 10, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    if ("series11" %in% completedSeries$Series) {
      series11 <- completedSeries[completedSeries$Series == "series11", ]$Winner
    } else {
      rs <- reseedTwoTeams(series5, series6, summary_results, currentSeries[currentSeries$SeriesID == 5, ]$HomeTeam)
      series11 <- single_series_solver(series_number = 11, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    if ("series12" %in% completedSeries$Series) {
      series12 <- completedSeries[completedSeries$Series == "series12", ]$Winner
    } else {
      rs <- reseedTwoTeams(series7, series8, summary_results, currentSeries[currentSeries$SeriesID == 7, ]$HomeTeam)
      series12 <- single_series_solver(series_number = 12, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    # Reseed for conference finals & stanley cup finals
    if ("series13" %in% completedSeries$Series) {
      series13 <- completedSeries[completedSeries$Series == "series13", ]$Winner
    } else {
      rs <- reseedTwoTeams(series9, series10, summary_results)
      series13 <- single_series_solver(series_number = 13, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    if ("series14" %in% completedSeries$Series) {
      series14 <- completedSeries[completedSeries$Series == "series14", ]$Winner
    } else {
      rs <- reseedTwoTeams(series11, series12, summary_results)
      series14 <- single_series_solver(series_number = 14, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }

    # Stanley Cup Final
    if ("series15" %in% completedSeries$Series) {
      series15 <- completedSeries[completedSeries$Series == "series15", ]$Winner
    } else {
      rs <- reseedTwoTeams(series13, series14, summary_results)
      series15 <- single_series_solver(series_number = 15, currentSeries = currentSeries, homeTeam = rs[1], awayTeam = rs[2], homeAwayOdds = homeAwayOdds)
    }
    srvec <- c(srvec, sim, l1, l2, l3, l4, l5, l6, l7, l8, series1, series2, series3, series4, series5, series6, series7, series8, series9, series10, series11, series12, series13, series14, series15)
    rm(l1, l2, l3, l4, l5, l6, l7, l8, series1, series2, series3, series4, series5, series6, series7, series8, series9, series10, series11, series12, series13, series14, series15)
  }
  srdf <- as.data.frame(matrix(srvec, ncol = 24, byrow = TRUE))
  names(srdf) <- names(simresults)
  simresults <- dplyr::as_tibble(srdf)
  return(simresults)
}

getAllHomeAwayOdds <- function(teamlist, params = NULL) {
  params <- parse_dc_params(params)
  homeAwayOdds <- expand.grid("HomeTeam" = teamlist, "AwayTeam" = teamlist, stringsAsFactors = FALSE)
  homeAwayOdds <- homeAwayOdds[homeAwayOdds$HomeTeam != homeAwayOdds$AwayTeam, ]
  homeAwayOdds$HomeOdds <- apply(homeAwayOdds, 1, function(x) playoffWin(x[1], x[2], params = params))
  return(homeAwayOdds)
}


#' Record Today's Predictions
#'
#' @description Record today's predictions to file (for easy later retrieval). Run \code{cleanupPredictionsFile} periodically to tidy
#'
#' @param today Day's predictions to record. Defaults to today, but can set any other day
#' @param filepath csv file location to store predictions. Will append to file.
#' @param schedule HockeyModel::schedule or supplied. \code{today} date must be in schedule
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#' @param include_xG Whether to record daily XG values
#' @param draws whether to record draw odds (True) or not (False). Default is True
#'
#' @return NULL
#' @export
recordTodaysPredictions <- function(today = Sys.Date(), filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv"), schedule = HockeyModel::schedule, params = NULL, include_xG = FALSE, draws = TRUE) {
  params <- parse_dc_params(params)
  stopifnot(is.Date(today))
  today <- as.Date(today)
  today_sched <- schedule[schedule$Date == today, ]
  if (nrow(today_sched) == 0) {
    stop("No games on date:", today)
  }
  today_preds <- todayDC(today = today, params = params, schedule = schedule, include_xG = include_xG, draws = draws)
  preds <- dplyr::full_join(today_sched, today_preds, suffix = c("", ""), by = c("HomeTeam", "AwayTeam"))
  if (!include_xG) {
    if (!draws) {
      preds <- preds[, c("Date", "GameID", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin")]
    } else {
      preds <- preds[, c("Date", "GameID", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw")]
    }
  } else {
    if (!draws) {
      preds <- preds[, c("Date", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Home_xG", "Away_xG")]
    } else {
      preds <- preds[, c("Date", "HomeTeam", "AwayTeam", "HomeWin", "AwayWin", "Draw", "Home_xG", "Away_xG")]
    }
  }

  if (file.exists(filepath)) {
    utils::write.table(preds, file = filepath, append = TRUE, col.names = FALSE, row.names = FALSE, sep = ",", dec = ".")
  } else {
    utils::write.table(preds, file = filepath, append = FALSE, col.names = TRUE, row.names = FALSE, sep = ",", dec = ".")
  }
}

#' Cleanup Predictions File
#'
#' @description Sometimes the predictions file may end up with game duplicates (last minute postponements, etc,) This deduplicates, taking only the latest instance of a prediction (Games are unique by GameID)
#'
#' @param filepath file path to cleanup
#'
#' @return NULL
#' @export
cleanupPredictionsFile <- function(filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv")) {
  dailyodds <- utils::read.csv(filepath)
  dailyodds <- dailyodds %>%
    dplyr::mutate("Date" = as.Date(.data$Date)) %>%
    dplyr::arrange(dplyr::desc(.data$Date)) %>%
    dplyr::distinct(.data$GameID, .keep_all = TRUE) %>%
    dplyr::arrange(.data$Date, .data$GameID) %>%
    utils::write.table(file = filepath, append = FALSE, col.names = TRUE, row.names = FALSE, sep = ",", dec = ".")

  return(TRUE)
}

build_past_predictions <- function(startDate, endDate, filepath = file.path(getOption("HockeyModel.data.path"), "dailyodds.csv"), include_xG = FALSE, draws = TRUE) {
  stopifnot(is.Date(startDate))
  stopifnot(is.Date(endDate))
  startDate <- as.Date(startDate)
  endDate <- as.Date(endDate)
  scores <- HockeyModel::scores
  schedule <- HockeyModel::schedule

  for (day in seq.Date(startDate, endDate, by = 1)) {
    d <- as.Date(day, origin = "1970-01-01")
    if (nrow(schedule[schedule$Date == d, ]) == 0) {
      next # no games that day, just skip it.
    }
    message("Results as of: ", d)
    score <- scores[scores$Date < day, ]
    score <- score[score$Date > (as.Date(startDate) - 4000), ] # only feed in ~ 11 years data to calculate m & rho
    sched <- schedule[schedule$Date == d, ]
    params <- list()
    params$m <- getM(scores = score, currentDate = d)
    params$rho <- getRho(m = params$m, scores = score)
    w.day <- getWeibullParams(m = params$m, rho = params$rho, scores = score)
    params$beta <- w.day$beta
    params$eta <- w.day$eta
    params$k <- w.day$k

    recordTodaysPredictions(today = d, filepath = filepath, schedule = sched, params = params, include_xG = include_xG, draws = draws)
  }
  cleanupPredictionsFile(filepath = filepath)
  return(TRUE)
}


#' Get Series Odds
#'
#' @param params The named list containing m, rho, beta, eta, and k. See [updateDC] for information on the params list
#'
#' @return NULL if no series are currently set but not complete, else a data frame.
#' @export
getSeriesOdds <- function(params = NULL) {
  series <- getAPISeries()

  if (is.na(series)) {
    return(NULL)
  }
  if (nrow(series) == 0) {
    return(NULL)
  }

  series <- series[series$Status != "Complete", ]


  if (nrow(series) == 0) {
    return(NULL)
  }

  params <- parse_dc_params(params)

  series$HomeSeed <- NULL
  series$AwaySeed <- NULL
  series$SeriesID <- NULL
  series$HomeOdds <- 0
  for (i in 1:nrow(series)) {
    series[i, ]$HomeOdds <- playoffWin(series[i, ]$HomeTeam, series[i, ]$AwayTeam,
      series[i, ]$HomeWins, series[i, ]$AwayWins,
      params = params
    )
  }
  series$AwayOdds <- 1 - series$HomeOdds

  return(series)
}
pbulsink/HockeyModel documentation built on Dec. 16, 2024, 8:03 a.m.