diff --git a/TSH.exe b/TSH.exe
index bde10700a..4bae7b215 100644
Binary files a/TSH.exe and b/TSH.exe differ
diff --git a/layout/bracket_ultimate/index.js b/layout/bracket_ultimate/index.js
index c72723754..12dd580a4 100644
--- a/layout/bracket_ultimate/index.js
+++ b/layout/bracket_ultimate/index.js
@@ -1335,7 +1335,7 @@ LoadEverything().then(() => {
Object.entries(bracket).filter(([round]) => parseInt(round) < 0)
);
- if (Object.keys(losersRounds).length % 2 == 0 && !allWinners) {
+ if (Object.keys(losersRounds).length % 2 == 0) {
Object.values(bracket["-1"].sets).forEach((set, index) => {
appearRounds.push([-1, index, 0]);
appearRounds.push([-1, index, 1]);
diff --git a/src/TSHBracket.py b/src/TSHBracket.py
index 5961befe7..b742b122a 100644
--- a/src/TSHBracket.py
+++ b/src/TSHBracket.py
@@ -2,6 +2,7 @@
from .Helpers.TSHLocaleHelper import TSHLocaleHelper
from loguru import logger
+
class BracketSet():
BYE = -1
PENDING = -2
@@ -20,32 +21,40 @@ def __init__(self, bracket: "Bracket", pos) -> None:
# Bracket always has a power of 2 number of players
# if there are less than that, we round up and add
# 'bye's as the lower seeded players
+
+
def next_power_of_2(x):
return 1 if x == 0 else 2**math.ceil(math.log2(x))
# Seeding order logic
+
+
def seeding(numPlayers):
rounds = math.log(numPlayers)/math.log(2)-1
- pls = [1,2]
+ pls = [1, 2]
for i in range(int(rounds)):
pls = nextLayer(pls)
return pls
# Checks if a number is power of 2
+
+
def is_power_of_two(n):
return (n != 0) and (n & (n-1) == 0)
+
def nextLayer(pls):
out = []
length = len(pls)*2+1
-
+
for d in pls:
out.append(d)
out.append(length-d)
return out
+
class Bracket():
- def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgressions=False, customSeeding=False) -> None:
+ def __init__(self, playerNumber, progressionsIn, seedMap=None, byeIds=[], winnersOnlyProgressions=False, customSeeding=False) -> None:
self.originalPlayerNumber = playerNumber
self.playerNumber = next_power_of_2(playerNumber)
@@ -58,13 +67,16 @@ def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgre
seeds = seedMap
else:
seeds = seeding(self.playerNumber)
-
+
self.seedMap = seeds
+ # List for byes in the middle of a custom seed
+ self.byeIds = byeIds
+
self.winnersOnlyProgressions = winnersOnlyProgressions
self.customSeeding = customSeeding
-
+
if progressionsIn > 0 and -1 in self.seedMap:
self.winnersOnlyProgressions = True
@@ -82,34 +94,37 @@ def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgre
_set.playerIds[0] = seeds[i]
_set.playerIds[1] = seeds[i+1]
self.rounds["1"].append(_set)
-
+
# Create losers
self.rounds["-1"] = []
self.rounds["-2"] = []
for i in range(int(self.playerNumber/2)):
- self.rounds["-1"].append(BracketSet(self, [-1, int(len(self.rounds["-1"])/2)]))
- self.rounds["-2"].append(BracketSet(self, [-1, int(len(self.rounds["-2"])/2)]))
-
+ self.rounds["-1"].append(BracketSet(self,
+ [-1, int(len(self.rounds["-1"])/2)]))
+ self.rounds["-2"].append(BracketSet(self,
+ [-1, int(len(self.rounds["-2"])/2)]))
+
# Fill with -1
for round in ["-1", "-2"]:
for _set in self.rounds[round]:
_set.score = [-1, -1]
_set.finished = True
-
+
# Expand winners
subBracket = []
i = self.playerNumber/2
while i > 1:
i = math.floor(i/2)
- round = [BracketSet(self, [2+len(subBracket), i]) for i in range(int(i))]
+ round = [BracketSet(self, [2+len(subBracket), i])
+ for i in range(int(i))]
subBracket.append(round)
subBracket.append([BracketSet(self, [2+len(subBracket), 0])])
subBracket.append([BracketSet(self, [2+len(subBracket), 0])])
for r, round in enumerate(subBracket):
self.rounds[str(2+r)] = round
-
+
# Expand losers
subBracket = []
i = self.playerNumber/2
@@ -117,7 +132,8 @@ def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgre
while i > 1:
i = math.floor(i/2)
for j in range(2):
- round = [BracketSet(self, [-1-len(subBracket), i]) for i in range(math.floor(i))]
+ round = [BracketSet(self, [-1-len(subBracket), i])
+ for i in range(math.floor(i))]
subBracket.append(round)
for r, round in enumerate(subBracket):
@@ -130,43 +146,53 @@ def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgre
if roundNum > 0:
for j, _set in enumerate(round):
try:
- _set.winNext = self.rounds[str(roundNum+1)][math.floor(j/2)]
- targetIdW = j%2
- if int(k) < 0 and abs(int(k))%2 == 1: targetIdW = 1
+ _set.winNext = self.rounds[str(
+ roundNum+1)][math.floor(j/2)]
+ targetIdW = j % 2
+ if int(k) < 0 and abs(int(k)) % 2 == 1:
+ targetIdW = 1
_set.winNextSlot = targetIdW
except Exception as e:
logger.error(e)
try:
- if abs(roundNum)%4 == 0:
- _set.loseNext = self.rounds[str(-int(2*(roundNum)))][(int(len(round)/2)+j)%len(round)]
- elif abs(roundNum)%4 == 1:
- _set.loseNext = self.rounds[str(-int(2*(roundNum)))][j]
- elif abs(roundNum)%4 == 2:
- _set.loseNext = self.rounds[str(-int(2*(roundNum)))][(-1-j)%len(round)]
- elif abs(roundNum)%4 == 3:
- _set.loseNext = self.rounds[str(-int(2*(roundNum)))][(int(len(round)/2)-1-j)%len(round)]
-
+ shift = 0
+
+ if (abs(roundNum) + shift) % 4 == 0:
+ _set.loseNext = self.rounds[str(-int(2*(roundNum)))][(
+ int(len(round)/2)+j) % len(round)]
+ elif (abs(roundNum) + shift) % 4 == 1:
+ _set.loseNext = self.rounds[str(
+ -int(2*(roundNum)))][j]
+ elif (abs(roundNum) + shift) % 4 == 2:
+ _set.loseNext = self.rounds[str(
+ -int(2*(roundNum)))][(-1-j) % len(round)]
+ elif (abs(roundNum) + shift) % 4 == 3:
+ _set.loseNext = self.rounds[str(-int(2*(roundNum)))][(
+ int(len(round)/2)-1-j) % len(round)]
+
targetIdL = 0
-
+
if roundNum == 1:
targetIdL = j % 2
-
+
_set.loseNextSlot = targetIdL
except Exception as e:
logger.error(e)
else:
for j, _set in enumerate(round):
try:
- if abs(roundNum)%2 == 0:
- _set.winNext = self.rounds[str(roundNum-1)][math.floor(j/2)]
+ if abs(roundNum) % 2 == 0:
+ _set.winNext = self.rounds[str(
+ roundNum-1)][math.floor(j/2)]
else:
_set.winNext = self.rounds[str(roundNum-1)][j]
- targetIdW = j%2
- if int(k) < 0 and abs(int(k))%2 == 1: targetIdW = 1
+ targetIdW = j % 2
+ if int(k) < 0 and abs(int(k)) % 2 == 1:
+ targetIdW = 1
_set.winNextSlot = targetIdW
except Exception as e:
logger.error(e)
-
+
# Connect losers to winners for grand finals
lastLosers = min([int(r) for r in self.rounds.keys()])
gfsRound = max([int(r) for r in self.rounds.keys()]) - 1
@@ -175,19 +201,23 @@ def __init__(self, playerNumber, progressionsIn, seedMap=None, winnersOnlyProgre
# Connect grand finals to reset
gfsResetRound = max([int(r) for r in self.rounds.keys()])
gfsRound = gfsResetRound - 1
- self.rounds[str(gfsRound)][0].winNext = self.rounds[str(gfsResetRound)][0]
- self.rounds[str(gfsRound)][0].loseNext = self.rounds[str(gfsResetRound)][0]
-
+ self.rounds[str(gfsRound)][0].winNext = self.rounds[str(
+ gfsResetRound)][0]
+ self.rounds[str(gfsRound)][0].loseNext = self.rounds[str(
+ gfsResetRound)][0]
+
def IsBye(self, playerId):
- if playerId == -1 or playerId > self.originalPlayerNumber: return True
+ if playerId == -1 or playerId > self.originalPlayerNumber:
+ return True
return False
-
+
def UpdateBracket(self):
for roundKey, round in sorted(self.rounds.items(), key=lambda x: (int(x[0]) < 0, abs(int(x[0])))):
for j, _set in enumerate(round):
- targetIdW = j%2
+ targetIdW = j % 2
targetIdL = 0
- if int(roundKey) < 0 and abs(int(roundKey))%2 == 1: targetIdW = 1
+ if int(roundKey) < 0 and abs(int(roundKey)) % 2 == 1:
+ targetIdW = 1
lastLosers = min([int(r) for r in self.rounds.keys()])
if roundKey == str(lastLosers):
@@ -197,22 +227,34 @@ def UpdateBracket(self):
if roundKey == str(gfsRound):
targetIdW = 0
targetIdL = 1
-
+
# When we have progressions in, force first (hidden) sets to double DQs
# If we have a non-power of 2 number of progressions, we do it for 2 rounds
if self.progressionsIn > 0 and not self.winnersOnlyProgressions:
if int(roundKey) == 1:
_set.score = [-1, -1]
_set.finished = True
-
- if int(roundKey) == 2 and not is_power_of_two(self.progressionsIn) and not self.customSeeding:
+
+ if int(roundKey) == 2 and (
+ (not is_power_of_two(self.progressionsIn) and not self.customSeeding) or
+ (self.customSeeding and not math.log2(self.playerNumber) % 2 == 0)):
_set.score = [-1, -1]
_set.finished = True
if _set.winNext:
+ # One of the players is a forced loss
+ if _set.playerIds[0] in self.byeIds or _set.playerIds[1] in self.byeIds:
+ won = 0
+ lost = 1
+ if _set.playerIds[0] in self.byeIds:
+ won = 1
+ lost = 0
+ _set.winNext.playerIds[targetIdW] = _set.playerIds[won]
+ if _set.loseNext:
+ _set.loseNext.playerIds[targetIdL] = _set.playerIds[lost]
# Both slots are bye OR slot 2 is bye, auto win for p1
- if (self.IsBye(_set.playerIds[0]) and self.IsBye(_set.playerIds[1])) or \
- (not self.IsBye(_set.playerIds[0]) and self.IsBye(_set.playerIds[1])):
+ elif (self.IsBye(_set.playerIds[0]) and self.IsBye(_set.playerIds[1])) or \
+ (not self.IsBye(_set.playerIds[0]) and self.IsBye(_set.playerIds[1])):
won = 0
lost = 1
_set.winNext.playerIds[targetIdW] = _set.playerIds[won]
@@ -228,7 +270,10 @@ def UpdateBracket(self):
# -1,-1 draw; advance higher seed
elif _set.score[0] == -1 and _set.score[1] == -1:
# Advance higher seed
- won = 0 if _set.playerIds[0] < _set.playerIds[1] else 1
+ found1 = self.seedMap.index(_set.playerIds[0])
+ found2 = self.seedMap.index(_set.playerIds[1])
+
+ won = 0 if found1 < found2 else 1
lost = 0 if won == 1 else 1
_set.winNext.playerIds[targetIdW] = _set.playerIds[won]
if _set.loseNext:
@@ -264,7 +309,7 @@ def UpdateBracket(self):
# _set.score[1] = 0
# Get round names
- def GetRoundName(self, round: str, winnersCutout=[0,0], losersCutout=[0,0]):
+ def GetRoundName(self, round: str, winnersCutout=[0, 0], losersCutout=[0, 0]):
roundNumber = int(round)
gfsRound = max([int(r) for r in self.rounds.keys()]) - 1
@@ -288,10 +333,10 @@ def GetRoundName(self, round: str, winnersCutout=[0,0], losersCutout=[0,0]):
return TSHLocaleHelper.matchNames.get("losers_semi_final")
if roundNumber == lastLosers + 2:
return TSHLocaleHelper.matchNames.get("losers_quarter_final")
-
+
if roundNumber > 0:
roundNumber -= winnersCutout[0]
return TSHLocaleHelper.matchNames.get("winners_round").format(abs(roundNumber))
if roundNumber < 0:
roundNumber += losersCutout[0]
- return TSHLocaleHelper.matchNames.get("losers_round").format(abs(roundNumber))
\ No newline at end of file
+ return TSHLocaleHelper.matchNames.get("losers_round").format(abs(roundNumber))
diff --git a/src/TSHBracketView.py b/src/TSHBracketView.py
index 20d3261df..872358cf2 100644
--- a/src/TSHBracketView.py
+++ b/src/TSHBracketView.py
@@ -8,17 +8,19 @@
import traceback
from loguru import logger
-# Checks if a number is power of 2
+
def is_power_of_two(n):
+ ''' Checks if a number is power of 2 '''
return (n != 0) and (n & (n-1) == 0)
+
class BracketSetWidget(QWidget):
def __init__(self, bracketSet: BracketSet = None, bracketView: "TSHBracketView" = None, *args) -> None:
super().__init__(*args)
self.setLayout(QVBoxLayout())
self.playerId: list(QLabel) = []
- self.name: list(QLineEdit) = []
+ self.name: list(QLabel) = []
self.score: list(QSpinBox) = []
self.bracketSet = bracketSet
@@ -27,24 +29,24 @@ def __init__(self, bracketSet: BracketSet = None, bracketView: "TSHBracketView"
for i in [0, 1]:
hbox = QWidget()
hbox.setLayout(QHBoxLayout())
- hbox.layout().setContentsMargins(0,0,0,0)
+ hbox.layout().setContentsMargins(0, 0, 0, 0)
hbox.layout().setSpacing(2)
self.layout().addWidget(hbox)
hbox.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
- idLabel = QLineEdit()
- idLabel.setDisabled(True)
+ idLabel = QLabel()
idLabel.setMinimumWidth(30)
idLabel.setMaximumWidth(30)
idLabel.setFont(QFont(idLabel.font().family(), 8))
idLabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
+ idLabel.setFrameShape(QFrame.Shape.StyledPanel)
self.playerId.append(idLabel)
hbox.layout().addWidget(idLabel)
- name = QLineEdit()
+ name = QLabel()
name.setMinimumWidth(120)
name.setMaximumWidth(120)
- name.setDisabled(True)
+ name.setFrameShape(QFrame.Shape.StyledPanel)
self.name.append(name)
hbox.layout().addWidget(name)
name.sizePolicy().setRetainSizeWhenHidden(True)
@@ -54,24 +56,33 @@ def __init__(self, bracketSet: BracketSet = None, bracketView: "TSHBracketView"
self.score.append(score)
hbox.layout().addWidget(score)
score.setValue(-1)
- score.valueChanged.connect(lambda newVal, i=i: self.SetScore(i, newVal))
+ score.valueChanged.connect(
+ lambda newVal, i=i: self.SetScore(i, newVal))
self.finished = QCheckBox("Finished")
self.layout().addWidget(self.finished)
- self.finished.toggled.connect(lambda newVal: self.SetFinished(newVal))
-
+ self.finished.toggled.connect(lambda newVal: self.SetFinished(newVal))
+
self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum)
self.sizePolicy().setRetainSizeWhenHidden(True)
self.layout().setSpacing(2)
- self.Update()
-
+ if self.bracketSet is not None:
+ hasBye = \
+ ((self.bracketSet.playerIds[0] == -1 and not self.bracketSet.playerIds[1] == -1) or
+ (self.bracketSet.playerIds[1] == -1 and not self.bracketSet.playerIds[0] == -1))
+
+ if self.bracketSet.pos[0] < 0 and hasBye:
+ self.hide()
+ elif self.bracketSet.pos[0] > 0 and self.bracketSet.pos[0] == 1 and hasBye:
+ self.hide()
+
def SetScore(self, id, score, updateDisplay=True):
self.bracketSet.score[id] = score
if updateDisplay:
self.bracketSet.bracket.UpdateBracket()
self.bracketView.Update()
-
+
def SetFinished(self, finished, updateDisplay=True):
self.bracketSet.finished = finished
@@ -88,31 +99,41 @@ def Update(self):
self.playerId[0].setText("")
if self.playerId[1].text() == "-2":
self.playerId[1].setText("")
-
+
self.score[0].blockSignals(True)
self.score[1].blockSignals(True)
- self.score[0].setValue(min(self.bracketSet.score[0], self.score[0].maximum()))
- self.score[1].setValue(min(self.bracketSet.score[1], self.score[1].maximum()))
+ self.score[0].setValue(
+ min(self.bracketSet.score[0], self.score[0].maximum()))
+ self.score[1].setValue(
+ min(self.bracketSet.score[1], self.score[1].maximum()))
self.score[0].blockSignals(False)
self.score[1].blockSignals(False)
if self.bracketSet.score[0] > self.bracketSet.score[1]:
- self.score[0].setStyleSheet("background-color: rgba(0, 255, 0, 50);")
- self.score[1].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
+ self.score[0].setStyleSheet(
+ "background-color: rgba(0, 255, 0, 50);")
+ self.score[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
elif self.bracketSet.score[0] < self.bracketSet.score[1]:
- self.score[1].setStyleSheet("background-color: rgba(0, 255, 0, 50);")
- self.score[0].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
+ self.score[1].setStyleSheet(
+ "background-color: rgba(0, 255, 0, 50);")
+ self.score[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
else:
- self.score[0].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
- self.score[1].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
-
+ self.score[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
+ self.score[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
+
try:
if (self.bracketSet.playerIds[0]-1) < len(self.bracketView.playerList.slotWidgets) and self.bracketSet.playerIds[0] > 0:
- team = StateManager.Get(f"bracket.players.slot.{self.bracketSet.playerIds[0]}", {})
+ team = StateManager.Get(
+ f"bracket.players.slot.{self.bracketSet.playerIds[0]}", {})
if team.get("name"):
teamName = team.get("name")
else:
- teamName = " / ".join([p.get("name", "") for p in team.get("player", {}).values()])
+ teamName = " / ".join([p.get("name", "")
+ for p in team.get("player", {}).values()])
self.name[0].setText(teamName)
else:
self.name[0].setText("")
@@ -121,11 +142,13 @@ def Update(self):
try:
if (self.bracketSet.playerIds[1]-1) < len(self.bracketView.playerList.slotWidgets) and self.bracketSet.playerIds[1] > 0:
- team = StateManager.Get(f"bracket.players.slot.{self.bracketSet.playerIds[1]}", {})
+ team = StateManager.Get(
+ f"bracket.players.slot.{self.bracketSet.playerIds[1]}", {})
if team.get("name"):
teamName = team.get("name")
else:
- teamName = " / ".join([p.get("name", "") for p in team.get("player", {}).values()])
+ teamName = " / ".join([p.get("name", "")
+ for p in team.get("player", {}).values()])
self.name[1].setText(teamName)
else:
self.name[1].setText("")
@@ -136,38 +159,51 @@ def Update(self):
self.finished.blockSignals(True)
self.finished.setChecked(self.bracketSet.finished)
self.finished.blockSignals(False)
-
+
winnersCutout, losersCutout = self.bracketView.GetCutouts()
hasBye = \
- ((self.bracketSet.playerIds[0] == -1 and not self.bracketSet.playerIds[1] == -1) or \
- (self.bracketSet.playerIds[1] == -1 and not self.bracketSet.playerIds[0] == -1))
+ ((self.bracketSet.playerIds[0] == -1 and not self.bracketSet.playerIds[1] == -1) or
+ (self.bracketSet.playerIds[1] == -1 and not self.bracketSet.playerIds[0] == -1))
if self.bracketSet.pos[0] < 0 and hasBye:
- self.hide()
+ if self.isVisible():
+ self.hide()
elif self.bracketSet.pos[0] > 0 and self.bracketSet.pos[0] == 1 and hasBye:
- self.hide()
+ if self.isVisible():
+ self.hide()
else:
- self.show()
-
+ if not self.isVisible():
+ self.show()
+
limitExportNumber, winnersOffset, losersOffset = self.bracketView.GetLimitedExportingBracketOffsets()
if self.bracketSet.pos[0] > 0:
- if self.bracketSet.pos[0] - winnersOffset <= 0:
- self.name[0].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
- self.name[1].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
+ if self.bracketSet.pos[0] - winnersOffset <= 0 or \
+ self.bracketSet.pos[0] == winnersCutout[0]+1 and self.bracketView.progressionsIn > 0 and self.bracketSet.score[0] == -1 or self.bracketSet.score[1] == -1:
+ self.name[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
+ self.name[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
else:
- self.name[0].setStyleSheet("background-color: rgba(0, 0, 0, 0);")
- self.name[1].setStyleSheet("background-color: rgba(0, 0, 0, 0);")
+ self.name[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 0);")
+ self.name[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 0);")
elif self.bracketSet.pos[0] < 0:
if self.bracketSet.pos[0] + losersOffset >= 0:
- self.name[0].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
- self.name[1].setStyleSheet("background-color: rgba(0, 0, 0, 80);")
+ self.name[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
+ self.name[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 80);")
else:
- self.name[0].setStyleSheet("background-color: rgba(0, 0, 0, 0);")
- self.name[1].setStyleSheet("background-color: rgba(0, 0, 0, 0);")
+ self.name[0].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 0);")
+ self.name[1].setStyleSheet(
+ "background-color: rgba(0, 0, 0, 0);")
+
class TSHBracketView(QGraphicsView):
- def __init__(self, bracket: Bracket, playerList: TSHPlayerList = None, bracketWidget = None, *args):
+ def __init__(self, bracket: Bracket, playerList: TSHPlayerList = None, bracketWidget=None, *args):
super().__init__(*args)
self.bracketWidget = bracketWidget
@@ -178,19 +214,19 @@ def __init__(self, bracket: Bracket, playerList: TSHPlayerList = None, bracketWi
self._empty = True
self._scene = QGraphicsScene(self)
self.setScene(self._scene)
-
+
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
- #self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
+ # self.setBackgroundBrush(QBrush(QColor(30, 30, 30)))
self.setFrameShape(QFrame.NoFrame)
self.setDragMode(QGraphicsView.ScrollHandDrag)
self.SetBracket(bracket)
self.bracketLines = []
-
+
def GetCutouts(self, forExport=False):
winnersRounds = [r for r in self.bracket.rounds.keys() if int(r) > 0]
losersRounds = [r for r in self.bracket.rounds.keys() if int(r) < 0]
@@ -200,42 +236,58 @@ def GetCutouts(self, forExport=False):
# Winners right side cutout
if self.progressionsOut > 0:
- progressionsWinners = math.pow(2, int(math.log2(self.progressionsOut/2)))
- winnersCutout[1] = len(winnersRounds) - (int(math.log2(progressionsWinners)) + 1)
-
+ progressionsWinners = math.pow(
+ 2, int(math.log2(self.progressionsOut/2)))
+ winnersCutout[1] = len(winnersRounds) - \
+ (int(math.log2(progressionsWinners)) + 1)
+
# Winners left side cutout
- if self.progressionsIn > 0 and not self.bracket.winnersOnlyProgressions:
- if not is_power_of_two(self.progressionsIn) and not self.bracket.customSeeding:
- winnersCutout[0] = 2
+ if self.progressionsIn > 0:
+ if self.bracket.winnersOnlyProgressions:
+ exactMatch = (int(math.log2((self.progressionsIn-1)))) + 3
+ winnersCutout[0] = len(winnersRounds) - exactMatch
else:
- winnersCutout[0] = 1
-
+ exactMatch = 1
+ if not is_power_of_two(self.progressionsIn) and not self.bracket.customSeeding:
+ exactMatch += 1
+ elif self.bracket.customSeeding and not math.log2(self.bracket.playerNumber) % 2 == 0:
+ exactMatch += 1
+ winnersCutout[0] = exactMatch
+
# Losers right side cutout
if self.progressionsOut > 0:
- progressionsLosers = self.progressionsOut - math.pow(2, int(math.log2(self.progressionsOut/2)))
- losersCutout[1] = len(losersRounds) - (math.log2(progressionsLosers) * 2 - 1)
-
- # Losers left side cutout
- losersCutout[0] = 2
+ progressionsLosers = self.progressionsOut - \
+ math.pow(2, int(math.log2(self.progressionsOut/2)))
+ losersCutout[1] = len(losersRounds) - \
+ (math.log2(progressionsLosers) * 2 - 1)
- # Losers R1 skipping
- # Losers R1 has bracket_size / 2 (half bracket is sent to losers from WR1) / 2 (2 players per set) sets
- # If WR1 has as many sets or less, LR1 will be all [player vs bye]
- # So this round is hidden
- validWR1Sets = self.bracket.originalPlayerNumber - self.bracket.playerNumber/2
-
- if not (self.bracketWidget.limitExport.isChecked() and self.bracketWidget.limitExportNumber.value() > 0):
- if self.progressionsIn == 0 and validWR1Sets <= self.bracket.playerNumber/2/2:
- losersCutout[0] += 1
+ # Losers left side cutout
+ if self.progressionsIn <= 0 or self.bracket.winnersOnlyProgressions:
+ exactMatch = int(math.log2(self.bracket.playerNumber)) + \
+ int(math.log2((self.bracket.originalPlayerNumber-1)/2))
+ losersCutout[0] = len(losersRounds) - exactMatch
+ else:
+ exactMatch = int(math.log2(self.bracket.playerNumber)) + \
+ round(math.log2(math.floor((self.bracket.progressionsIn -
+ int(math.log2(self.bracket.originalPlayerNumber-1)))))) - 2
+ losersCutout[0] = len(losersRounds) - exactMatch
+
+ # else:
+ # # Losers R1 skipping
+ # # Losers R1 has bracket_size / 2 (half bracket is sent to losers from WR1) / 2 (2 players per set) sets
+ # # If WR1 has as many sets or less, LR1 will be all [player vs bye]
+ # # So this round is hidden
+ # validWR1Sets = self.bracket.originalPlayerNumber - self.bracket.playerNumber/2
+
+ # if self.progressionsIn == 0 and validWR1Sets <= self.bracket.playerNumber/2/2:
+ # losersCutout[0] += 1
+ # elif self.progressionsIn > 0:
+ # losersCutout[0] += 1
- if self.progressionsIn > 0 and not is_power_of_two(self.progressionsIn):
- losersCutout[0] += 1
-
if self.progressionsOut > 0 and not is_power_of_two(self.progressionsOut):
losersCutout[1] += 1
-
- return (winnersCutout, losersCutout)
+ return (winnersCutout, losersCutout)
def SetBracket(self, bracket, progressionsIn=0, progressionsOut=0, winnersOnlyProgressions=False, customSeeding=False):
self.bracket = bracket
@@ -246,7 +298,7 @@ def SetBracket(self, bracket, progressionsIn=0, progressionsOut=0, winnersOnlyPr
bracket.winnersOnlyProgressions = winnersOnlyProgressions
else:
bracket.winnersOnlyProgressions = True
-
+
bracket.customSeeding = customSeeding
self.bracketLines = []
@@ -281,21 +333,25 @@ def SetBracket(self, bracket, progressionsIn=0, progressionsOut=0, winnersOnlyPr
for roundNum, round in self.bracket.rounds.items():
currentBracket = self.winnersBracket
currentWidgets = self.winnersBracketWidgets
-
+
if int(roundNum) < 0:
currentBracket = self.losersBracket
currentWidgets = self.losersBracketWidgets
-
+
# Winners cutout
if int(roundNum) > 0:
- if int(roundNum) <= winnersCutout[0]: continue
- if int(roundNum) >= winnersCutout[1]: continue
-
+ if int(roundNum) <= winnersCutout[0]:
+ continue
+ if int(roundNum) >= winnersCutout[1]:
+ continue
+
# Losers cutout
if int(roundNum) < 0:
- if abs(int(roundNum)) <= losersCutout[0]: continue
- if abs(int(roundNum)) >= losersCutout[1]: continue
-
+ if abs(int(roundNum)) <= losersCutout[0]:
+ continue
+ if abs(int(roundNum)) >= losersCutout[1]:
+ continue
+
# Outer Round layout (column)
layoutOuter = QWidget()
currentBracket.layout().addWidget(layoutOuter)
@@ -303,7 +359,8 @@ def SetBracket(self, bracket, progressionsIn=0, progressionsOut=0, winnersOnlyPr
# Round name
roundNameLabel = QLineEdit()
- roundNameLabel.setPlaceholderText(self.bracket.GetRoundName(int(roundNum), winnersCutout, losersCutout))
+ roundNameLabel.setPlaceholderText(self.bracket.GetRoundName(
+ int(roundNum), winnersCutout, losersCutout))
layoutOuter.layout().addWidget(roundNameLabel)
self.roundNameLabels[roundNum] = roundNameLabel
@@ -318,7 +375,7 @@ def SetBracket(self, bracket, progressionsIn=0, progressionsOut=0, winnersOnlyPr
roundWidgets.append(wid)
self.bracketWidgets.append(roundWidgets)
currentWidgets.append(roundWidgets)
-
+
QGuiApplication.processEvents()
self.DrawLines()
self.fitInView()
@@ -337,25 +394,28 @@ def GetLimitedExportingBracketOffsets(self):
winnersRounds = math.floor(limitExportNumber/8) + 3
try:
- losersRounds = int(math.log2(limitExportNumber)) + int(math.log2((limitExportNumber-1)/2)) + 2
+ losersRounds = int(math.log2(limitExportNumber)) + \
+ int(math.log2((limitExportNumber-1)/2)) + 2
except:
logger.error(traceback.format_exc())
losersRounds = 0
if self.bracketWidget.progressionsIn.value() > 0:
StateManager.Set("bracket.bracket.progressionsIn", 0)
- losersRounds += 2
- winnersRounds += 1
-
- totalWinnersRounds = len([i for i in self.bracket.rounds.keys() if int(i) > 0])
- totalLosersRounds = len([i for i in self.bracket.rounds.keys() if int(i) < 0])
+ # losersRounds += 2
+ # winnersRounds += 1
+
+ totalWinnersRounds = len(
+ [i for i in self.bracket.rounds.keys() if int(i) > 0])
+ totalLosersRounds = len(
+ [i for i in self.bracket.rounds.keys() if int(i) < 0])
if self.bracketWidget.limitExport.isChecked():
winnersOffset = (totalWinnersRounds-winnersRounds)
losersOffset = (totalLosersRounds-losersRounds)
return (limitExportNumber, winnersOffset, losersOffset)
-
+
def Update(self):
self.bracket.UpdateBracket()
@@ -366,57 +426,73 @@ def Update(self):
setWidget.Update()
for w in setWidget.score:
w.blockSignals(False)
-
- QGuiApplication.processEvents()
+ QGuiApplication.processEvents()
self.DrawLines()
-
+
StateManager.BlockSaving()
data = {}
- StateManager.Set("bracket.bracket.progressionsIn", self.bracket.progressionsIn)
- StateManager.Set("bracket.bracket.progressionsOut", self.bracketWidget.progressionsOut.value())
+ StateManager.Set("bracket.bracket.progressionsIn",
+ self.bracket.progressionsIn)
+ StateManager.Set("bracket.bracket.progressionsOut",
+ self.bracketWidget.progressionsOut.value())
limitExportNumber, winnersOffset, losersOffset = self.GetLimitedExportingBracketOffsets()
winnersCutout, losersCutout = self.GetCutouts(forExport=True)
- winnersOffset += winnersCutout[0]
- losersOffset += losersCutout[0]
+ # winnersOffset += winnersCutout[0]
+ # losersOffset += losersCutout[0]
- StateManager.Set("bracket.bracket.limitExportNumber", limitExportNumber)
+ StateManager.Set("bracket.bracket.limitExportNumber",
+ limitExportNumber)
if limitExportNumber != -1 and limitExportNumber < self.bracket.playerNumber:
StateManager.Set("bracket.bracket.winnersOnlyProgressions", False)
else:
- StateManager.Set("bracket.bracket.winnersOnlyProgressions", self.bracket.winnersOnlyProgressions)
+ StateManager.Set("bracket.bracket.winnersOnlyProgressions",
+ self.bracket.winnersOnlyProgressions)
for roundKey, round in self.bracket.rounds.items():
# Winners cutout
if int(roundKey) > 0:
- if int(roundKey) <= winnersCutout[0]: continue
- if int(roundKey) >= winnersCutout[1]: continue
-
+ if int(roundKey) <= winnersCutout[0]:
+ continue
+ if int(roundKey) >= winnersCutout[1]:
+ continue
+
# Losers cutout
if int(roundKey) < 0:
- if abs(int(roundKey)) <= losersCutout[0]: continue
- if abs(int(roundKey)) >= losersCutout[1]: continue
-
+ if abs(int(roundKey)) <= losersCutout[0]:
+ continue
+ if abs(int(roundKey)) >= losersCutout[1]:
+ continue
+
+ # Limited export number cutout
+ if int(roundKey) > 0:
+ if int(roundKey) - winnersOffset <= 0:
+ continue
+ if int(roundKey) < 0:
+ if int(roundKey) + losersOffset + 2 >= 0:
+ continue
+
# Get round name or placeholder name
if self.roundNameLabels.get(roundKey):
roundName = self.roundNameLabels.get(roundKey).text()
if roundName == "":
- roundName = self.roundNameLabels.get(roundKey).placeholderText()
+ roundName = self.roundNameLabels.get(
+ roundKey).placeholderText()
else:
roundName = ""
-
- # Limited export number cutout
+
+ # Reassign round key
if int(roundKey) > 0:
- roundKey = str(int(roundKey) - winnersOffset)
- if int(roundKey) <= 0: continue
- if int(roundKey) < 0:
- roundKey = str(int(roundKey) + losersOffset)
- if int(roundKey) >= 0: continue
+ roundKey = str(int(roundKey) -
+ max(winnersOffset, winnersCutout[0]))
+ else:
+ roundKey = str(int(roundKey) +
+ max(losersOffset + 2, losersCutout[0]))
data[roundKey] = {
"name": roundName,
@@ -432,12 +508,14 @@ def Update(self):
# Reassign rounds based on export number
if nextWin:
if nextWin[0] > 0:
- nextWin[0] -= winnersOffset
+ nextWin[0] -= max(winnersOffset, winnersCutout[0])
else:
- nextWin[0] += losersOffset - 2
+ nextWin[0] += max(losersOffset + 2,
+ losersCutout[0]) - 2
if nextLose:
if nextLose[0] < 0:
- nextLose[0] += losersOffset - 2
+ nextLose[0] += max(losersOffset + 2,
+ losersCutout[0]) - 2
"""if self.bracket.progressionsIn <= 0:
nextLose[0] -= 1
@@ -446,13 +524,15 @@ def Update(self):
# For grand finals into reset, nextLose is a positive round
else:
nextLose[0] -= winnersOffset
-
+
p1name = ""
if bracketSet.playerIds[0] > 0:
try:
- p1tree: dict = StateManager.Get(f"bracket.players.slot.{bracketSet.playerIds[0]}.player")
- p1name = " / ".join([p.get("name") for p in p1tree.values()])
+ p1tree: dict = StateManager.Get(
+ f"bracket.players.slot.{bracketSet.playerIds[0]}.player")
+ p1name = " / ".join([p.get("name")
+ for p in p1tree.values()])
except:
pass
@@ -460,14 +540,29 @@ def Update(self):
if bracketSet.playerIds[1] > 0:
try:
- p2tree: dict = StateManager.Get(f"bracket.players.slot.{bracketSet.playerIds[1]}.player")
- p2name = " / ".join([p.get("name") for p in p2tree.values()])
+ p2tree: dict = StateManager.Get(
+ f"bracket.players.slot.{bracketSet.playerIds[1]}.player")
+ p2name = " / ".join([p.get("name")
+ for p in p2tree.values()])
except:
pass
+ playerIds = [bracketSet.playerIds[0], bracketSet.playerIds[1]]
+ score = [bracketSet.score[0], bracketSet.score[1]]
+
+ if bracketSet.score[0] == -1 or bracketSet.score[1] == -1:
+ if self.progressionsIn > 0 and int(roundKey) == 1 and bracketSet.pos[0] == winnersCutout[0]+1:
+ if bracketSet.score[0] == -1:
+ playerIds[0] = -1
+ else:
+ playerIds[1] = -1
+ p1name = ""
+ p2name = ""
+ score = [-1, -1]
+
data[roundKey]["sets"][j] = {
- "playerId": bracketSet.playerIds,
- "score": bracketSet.score,
+ "playerId": playerIds,
+ "score": score,
"nextWin": nextWin,
"winSlot": bracketSet.winNextSlot,
"nextLose": nextLose,
@@ -480,9 +575,8 @@ def Update(self):
}
StateManager.Set("bracket.bracket.rounds", data)
-
StateManager.ReleaseSaving()
-
+
def DrawLines(self):
for element in self.bracketLines:
self._scene.removeItem(element)
@@ -501,7 +595,8 @@ def DrawLines(self):
if i < len(self.winnersBracketWidgets)-1:
- nxtWidget = self.winnersBracketWidgets[i+1][math.floor(j/2)]
+ nxtWidget = self.winnersBracketWidgets[i +
+ 1][math.floor(j/2)]
nxt = nxtWidget
start = QPointF(setWidget.mapTo(self.bracketLayout, QPoint(0, 0)).x(), _set.mapTo(self.bracketLayout, QPoint(0, 0)).y()) + \
@@ -509,8 +604,10 @@ def DrawLines(self):
end = QPointF(nxtWidget.mapTo(self.bracketLayout, QPoint(0, 0)).x(), nxt.mapTo(self.bracketLayout, QPoint(0, 0)).y()) + \
QPointF(0, _set.height()/2)
- midpoint1 = QPointF(start.x()+(end.x()-start.x())/2, start.y())
- midpoint2 = QPointF(start.x()+(end.x()-start.x())/2, end.y())
+ midpoint1 = QPointF(
+ start.x()+(end.x()-start.x())/2, start.y())
+ midpoint2 = QPointF(
+ start.x()+(end.x()-start.x())/2, end.y())
path.addPolygon(
QPolygonF([
@@ -527,7 +624,7 @@ def DrawLines(self):
notch1 = end + QPointF(-10, +10)
notch2 = end + QPointF(-10, -10)
-
+
path.addPolygon(
QPolygonF([
start,
@@ -547,14 +644,14 @@ def DrawLines(self):
end = QPointF(setWidget.mapTo(self.bracketLayout, QPoint(0, 0)).x(), _set.mapTo(self.bracketLayout, QPoint(0, 0)).y()) + \
QPointF(0, _set.height()/2)
start = end - QPointF(50, 0)
-
+
dashedPath.addPolygon(
QPolygonF([
start,
end
])
)
-
+
for i, round in enumerate(self.losersBracketWidgets):
for j, setWidget in enumerate(round):
try:
@@ -565,7 +662,8 @@ def DrawLines(self):
if i < len(self.losersBracketWidgets)-1:
if len(self.losersBracketWidgets[i+1]) < len(self.losersBracketWidgets[i]):
- nxtWidget = self.losersBracketWidgets[i+1][math.floor(j/2)]
+ nxtWidget = self.losersBracketWidgets[i +
+ 1][math.floor(j/2)]
else:
nxtWidget = self.losersBracketWidgets[i+1][j]
nxt = nxtWidget
@@ -575,8 +673,10 @@ def DrawLines(self):
end = QPointF(nxtWidget.mapTo(self.bracketLayout, QPoint(0, 0)).x(), nxt.mapTo(self.bracketLayout, QPoint(0, 0)).y()) + \
QPointF(0, _set.height()/2)
- midpoint1 = QPointF(start.x()+(end.x()-start.x())/2, start.y())
- midpoint2 = QPointF(start.x()+(end.x()-start.x())/2, end.y())
+ midpoint1 = QPointF(
+ start.x()+(end.x()-start.x())/2, start.y())
+ midpoint2 = QPointF(
+ start.x()+(end.x()-start.x())/2, end.y())
path.addPolygon(
QPolygonF([
@@ -593,7 +693,7 @@ def DrawLines(self):
notch1 = end + QPointF(-10, +10)
notch2 = end + QPointF(-10, -10)
-
+
path.addPolygon(
QPolygonF([
start,
@@ -613,7 +713,7 @@ def DrawLines(self):
end = QPointF(setWidget.mapTo(self.bracketLayout, QPoint(0, 0)).x(), _set.mapTo(self.bracketLayout, QPoint(0, 0)).y()) + \
QPointF(0, _set.height()/2)
start = end - QPointF(50, 0)
-
+
dashedPath.addPolygon(
QPolygonF([
start,
@@ -622,7 +722,7 @@ def DrawLines(self):
)
except:
logger.error(traceback.format_exc())
-
+
pen = QPen(Qt.gray, 4, Qt.SolidLine)
pen2 = QPen(Qt.black, 6, Qt.SolidLine)
@@ -647,18 +747,18 @@ def fitInView(self):
viewrect = self.viewport().rect()
scenerect = self.transform().mapRect(rect)
factor = min(viewrect.width() / scenerect.width(),
- viewrect.height() / scenerect.height())
+ viewrect.height() / scenerect.height())
self.scale(factor, factor)
self._zoom = 0
def wheelEvent(self, event):
if event.angleDelta().y() > 0:
- factor = 1.25
+ factor = 1.02
self._zoom += 1
else:
- factor = 0.8
+ factor = 0.98
self._zoom -= 1
-
+
if self._zoom > 0:
self.scale(factor, factor)
else:
@@ -666,4 +766,4 @@ def wheelEvent(self, event):
self._zoom = 0
def mousePressEvent(self, event):
- super().mousePressEvent(event)
\ No newline at end of file
+ super().mousePressEvent(event)
diff --git a/src/TSHBracketWidget.py b/src/TSHBracketWidget.py
index ff97dc713..b55fe7c1b 100644
--- a/src/TSHBracketWidget.py
+++ b/src/TSHBracketWidget.py
@@ -13,6 +13,7 @@
from .TSHPlayerList import TSHPlayerList
from .TSHBracket import *
import traceback
+import threading
from loguru import logger
# Checks if a number is power of 2
@@ -35,6 +36,8 @@ def __init__(self, *args):
StateManager.Set("bracket", {})
+ self.loadLock = threading.RLock()
+
TSHTournamentDataProvider.instance.signals.tournament_phases_updated.connect(
self.UpdatePhases)
TSHTournamentDataProvider.instance.signals.tournament_phasegroup_updated.connect(
@@ -219,7 +222,8 @@ def UpdatePhaseGroups(self):
self.phaseGroupSelection.clear()
if self.phaseSelection.currentData() != None:
- logger.info(str(self.phaseSelection.currentData().get("groups", [])))
+ logger.info(
+ str(self.phaseSelection.currentData().get("groups", [])))
for phaseGroup in self.phaseSelection.currentData().get("groups", []):
self.phaseGroupSelection.addItem(
phaseGroup.get("name"), phaseGroup)
@@ -246,9 +250,15 @@ def PhaseGroupChanged(self):
TSHTournamentDataProvider.instance.GetTournamentPhaseGroup(
self.phaseGroupSelection.currentData().get("id"))
- def RebuildBracket(self, playerNumber, seedMap=None, customSeeding=False):
- self.bracket = Bracket(playerNumber, self.progressionsIn.value(
- ), seedMap, self.winnersOnly.isChecked())
+ def RebuildBracket(self, playerNumber, seedMap=None, byeIds=[], customSeeding=False):
+ self.bracket = Bracket(
+ playerNumber,
+ self.progressionsIn.value(),
+ seedMap,
+ byeIds,
+ self.winnersOnly.isChecked(),
+ customSeeding=customSeeding
+ )
self.bracketView.SetBracket(
self.bracket,
@@ -265,29 +275,40 @@ def RebuildBracket(self, playerNumber, seedMap=None, customSeeding=False):
_set.finished = False
def UpdatePhaseGroup(self, phaseGroupData):
- StateManager.BlockSaving()
- self.playerList.signals.DataChanged.disconnect()
+ with self.loadLock:
+ StateManager.BlockSaving()
+ self.playerList.signals.DataChanged.disconnect()
try:
logger.info("Phase Group Data: " + str(phaseGroupData))
if phaseGroupData.get("progressionsIn", {}) != None:
+ self.progressionsIn.blockSignals(True)
self.progressionsIn.setValue(
len(phaseGroupData.get("progressionsIn", {})))
+ self.progressionsIn.blockSignals(False)
else:
+ self.progressionsIn.blockSignals(True)
self.progressionsIn.setValue(0)
+ self.progressionsIn.blockSignals(False)
if phaseGroupData.get("progressionsOut", {}) != None:
+ self.progressionsOut.blockSignals(True)
self.progressionsOut.setValue(
len(phaseGroupData.get("progressionsOut", {})))
+ self.progressionsIn.blockSignals(False)
else:
self.progressionsOut.setValue(0)
if phaseGroupData.get("winnersOnlyProgressions", False) != None:
+ self.winnersOnly.blockSignals(True)
self.winnersOnly.setChecked(
phaseGroupData.get("winnersOnlyProgressions", False))
+ self.winnersOnly.blockSignals(False)
else:
+ self.winnersOnly.blockSignals(True)
self.winnersOnly.setChecked(False)
+ self.winnersOnly.blockSignals(False)
self.bracket.customSeeding = phaseGroupData.get(
"customSeeding", False)
@@ -295,7 +316,8 @@ def UpdatePhaseGroup(self, phaseGroupData):
# Make sure progressions are exported
QGuiApplication.processEvents()
- self.playerList.LoadFromStandings(phaseGroupData.get("entrants"))
+ self.playerList.LoadFromStandings(
+ phaseGroupData.get("entrants"))
# Wait for the player list to update
QGuiApplication.processEvents()
@@ -311,6 +333,7 @@ def UpdatePhaseGroup(self, phaseGroupData):
self.RebuildBracket(
len(phaseGroupData.get("entrants")),
phaseGroupData.get("seedMap"),
+ phaseGroupData.get("byeIds", []),
phaseGroupData.get("customSeeding", False)
)
diff --git a/src/TournamentDataProvider/ChallongeDataProvider.py b/src/TournamentDataProvider/ChallongeDataProvider.py
index 60fc3f357..acc0484ff 100644
--- a/src/TournamentDataProvider/ChallongeDataProvider.py
+++ b/src/TournamentDataProvider/ChallongeDataProvider.py
@@ -63,7 +63,7 @@ def __init__(self, url, threadpool, parent) -> None:
i += 1
if i >= max_iter:
raise e
- #TODO: Find a way to open a warning box and unload tournament if failed
+ # TODO: Find a way to open a warning box and unload tournament if failed
def GetSlug(self):
# URL with language
@@ -308,6 +308,8 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None):
if len(groups) > 0:
finalData["progressionsIn"] = [
{}] * deep_get(groups[0], "tournament.participant_count_to_advance", 0) * len(groups)
+
+ finalData["customSeeding"] = True
else:
if id != None:
groupId = int(id.split("_")[-1])
@@ -448,7 +450,7 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None):
rounds[int(r)-1] = rounds[r]
# If we had progressions in, we have to add a fake R1 to send half players to losers side
- if len(finalData.get("progressionsIn", [])) > 0:
+ if len(finalData.get("progressionsIn", [])) > 0 and not finalData.get("winnersOnlyProgressions"):
sortedRounds = list(rounds.keys())
sortedRounds.sort(key=lambda x: int(x), reverse=True)
for r in sortedRounds:
diff --git a/src/TournamentDataProvider/StartGGDataProvider.py b/src/TournamentDataProvider/StartGGDataProvider.py
index f432cd1b4..5dc1ae4a5 100644
--- a/src/TournamentDataProvider/StartGGDataProvider.py
+++ b/src/TournamentDataProvider/StartGGDataProvider.py
@@ -14,7 +14,8 @@
from .TournamentDataProvider import TournamentDataProvider
import json
from ..Helpers.TSHLocaleHelper import TSHLocaleHelper
-from ..TSHBracket import is_power_of_two
+from ..TSHBracket import is_power_of_two, next_power_of_2
+import math
from ..Workers import Worker
import sys
@@ -199,14 +200,39 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None):
)
oldData = json.loads(oldData.text)
- seeds = deep_get(data, "data.phaseGroup.seeds.nodes", [])
- seeds.sort(key=lambda s: s.get("seedNum"))
+ seeds: list = deep_get(data, "data.phaseGroup.seeds.nodes", [])
+ seeds.sort(key=lambda s: s.get("groupSeedNum"))
+
+ # for s in seeds:
+ # s["seedNum"] = s.get("groupSeedNum")
+
+ # StartGG hides empty seeds. Fix that
+ fixedSeeds = []
+
+ lastSeed = 0
+
+ byeIds = []
+
+ while len(seeds) > 0:
+ minSeed = min(seeds, key=lambda s: s.get("groupSeedNum"))
+
+ while minSeed.get("groupSeedNum") > lastSeed+1:
+ fixedSeeds.append({})
+ lastSeed += 1
+ byeIds.append(lastSeed)
+ else:
+ fixedSeeds.append(minSeed)
+ seeds.remove(minSeed)
+ lastSeed = minSeed.get("groupSeedNum")
+
+ seeds = fixedSeeds
seedMap: list = deep_get(data, "data.phaseGroup.seedMap.1")
if seedMap:
seedMap = [s if s != "bye" else -1 for s in seedMap]
finalData["seedMap"] = seedMap
+ finalData["byeIds"] = byeIds
teams = []
@@ -252,8 +278,16 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None):
if not str(round) in finalSets:
finalSets[str(round)] = []
+ score = [s.get("entrant1Score"), s.get("entrant2Score")]
+
+ if s.get("entrant1Score") == None and s.get("entrant2Score") == None and s.get("winnerId") != None and s.get("state", 0) == 3:
+ if s.get("winnerId") == s.get("entrant1Id"):
+ score = [1, 0]
+ else:
+ score = [0, 1]
+
finalSets[str(round)].append({
- "score": [s.get("entrant1Score"), s.get("entrant2Score")],
+ "score": score,
"finished": s.get("state", 0) == 3
})
@@ -291,7 +325,8 @@ def GetTournamentPhaseGroup(self, id, progress_callback=None):
len(finalData["progressionsIn"])) else 2
if deep_get(oldData, "entities.groups.hasCustomWinnerByes"):
- shift = 1
+ shift = 1 if math.log2(
+ next_power_of_2(len(teams))) % 2 == 0 else 2
for roundKey in originalKeys:
round = int(roundKey)
@@ -614,10 +649,8 @@ def ParseMatchDataNewApi(self, _set):
player.get("id"),
0
]
- if playerData.get("id") and len(playerData.get("id")) > 0:
- if playerData["id"][0] is not None:
- playerData["seed"] = self.player_seeds.get(
- playerData["id"][0])
+ if playerData["id"][0] is not None:
+ playerData["seed"] = self.player_seeds[playerData["id"][0]]
players[i].append(playerData)
@@ -900,7 +933,6 @@ def GetStreamQueue(self, progress_callback=None):
queueData = {}
for setIndex, _set in enumerate(q.get("sets", [])):
-
phase_name = deep_get(_set, "phaseGroup.phase.name")
if deep_get(_set, "phaseGroup.phase.groupCount") > 1:
phase_name += " - " + TSHLocaleHelper.phaseNames.get(
@@ -1457,7 +1489,7 @@ def GetEntrantsWorker(self, eventSlug, gameId, progress_callback):
for j, entrant in enumerate(team.get("participants", [])):
playerData = StartGGDataProvider.ProcessEntrantData(
entrant)
- if deep_get(team, "seeds", []) is not None and len(deep_get(team, "seeds", [])) > 0:
+ if deep_get(team, "seeds", []) != []:
playerData["seed"] = deep_get(team, "seeds", [])[
0].get("seedNum", 0)
self.player_seeds[playerData["id"]
diff --git a/src/TournamentDataProvider/StartGGTournamentPhaseGroupQuery.txt b/src/TournamentDataProvider/StartGGTournamentPhaseGroupQuery.txt
index 053c5762c..0edff54c8 100644
--- a/src/TournamentDataProvider/StartGGTournamentPhaseGroupQuery.txt
+++ b/src/TournamentDataProvider/StartGGTournamentPhaseGroupQuery.txt
@@ -4,6 +4,7 @@ query TournamentPhaseGroupQuery($id: ID!, $videogameId: ID) {
seeds(query: {page: 1, perPage: 500}) {
nodes {
seedNum
+ groupSeedNum
progressionSource {
originPhaseGroup {
id
@@ -18,22 +19,6 @@ query TournamentPhaseGroupQuery($id: ID!, $videogameId: ID) {
gamerTag
prefix
name
- sets(page: 1, perPage: 1, filters: { hideEmpty: true, videogameId: $videogameId, videogameIds: [$videogameId] }) {
- nodes {
- games {
- selections {
- entrant {
- participants {
- player {
- id
- }
- }
- }
- selectionValue
- }
- }
- }
- }
}
user {
id
@@ -60,7 +45,9 @@ query TournamentPhaseGroupQuery($id: ID!, $videogameId: ID) {
nodes {
id
round
- identifier
+ entrant1Id
+ entrant2Id
+ winnerId
entrant1Score
entrant2Score
state
diff --git a/src/i18n/TSH_de.ts b/src/i18n/TSH_de.ts
index 64371be4e..584d864b8 100644
--- a/src/i18n/TSH_de.ts
+++ b/src/i18n/TSH_de.ts
@@ -709,20 +709,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
Zahl der Slots
-
+
Players per slot
Spieler pro Slot
-
+
Characters per player
Charaktere pro Slot
diff --git a/src/i18n/TSH_en.ts b/src/i18n/TSH_en.ts
index 75b9b9544..578aca6ee 100644
--- a/src/i18n/TSH_en.ts
+++ b/src/i18n/TSH_en.ts
@@ -704,20 +704,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
-
+
Players per slot
-
+
Characters per player
diff --git a/src/i18n/TSH_es.ts b/src/i18n/TSH_es.ts
index 314400bc5..d0812c693 100644
--- a/src/i18n/TSH_es.ts
+++ b/src/i18n/TSH_es.ts
@@ -714,20 +714,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
Número de grupos
-
+
Players per slot
Jugadores por grupo
-
+
Characters per player
Personajes por jugador
diff --git a/src/i18n/TSH_fr.ts b/src/i18n/TSH_fr.ts
index 6090bfd15..c1de7f2d9 100644
--- a/src/i18n/TSH_fr.ts
+++ b/src/i18n/TSH_fr.ts
@@ -1067,20 +1067,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
Nombre d'emplacements
-
+
Players per slot
Nombre de joueurs par emplacement
-
+
Characters per player
Nombre de personnages par joueur
diff --git a/src/i18n/TSH_it.ts b/src/i18n/TSH_it.ts
index a3c8a9df4..fcd031025 100644
--- a/src/i18n/TSH_it.ts
+++ b/src/i18n/TSH_it.ts
@@ -1013,20 +1013,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
-
+
Players per slot
-
+
Characters per player
Personaggi per giocatore
diff --git a/src/i18n/TSH_ja.ts b/src/i18n/TSH_ja.ts
index 8eae4997f..04922e453 100644
--- a/src/i18n/TSH_ja.ts
+++ b/src/i18n/TSH_ja.ts
@@ -1039,20 +1039,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
スロットの数
-
+
Players per slot
各スロットのプレイヤー数
-
+
Characters per player
各プレイヤーの使用キャラクター数
diff --git a/src/i18n/TSH_pt-BR.ts b/src/i18n/TSH_pt-BR.ts
index bc87e414a..8ce85d6b8 100644
--- a/src/i18n/TSH_pt-BR.ts
+++ b/src/i18n/TSH_pt-BR.ts
@@ -1086,20 +1086,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
Número de grupos
-
+
Players per slot
Jogadores por grupo
-
+
Characters per player
Personagens por jogador
diff --git a/src/i18n/TSH_zh-CN.ts b/src/i18n/TSH_zh-CN.ts
index 4a40a39d6..be49fca93 100644
--- a/src/i18n/TSH_zh-CN.ts
+++ b/src/i18n/TSH_zh-CN.ts
@@ -1013,20 +1013,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
-
+
Players per slot
-
+
Characters per player
diff --git a/src/i18n/TSH_zh-TW.ts b/src/i18n/TSH_zh-TW.ts
index b4f2129ca..3891a1231 100644
--- a/src/i18n/TSH_zh-TW.ts
+++ b/src/i18n/TSH_zh-TW.ts
@@ -1013,20 +1013,20 @@ p, li { white-space: pre-wrap; }
-
+
Number of slots
-
+
Players per slot
-
+
Characters per player