Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
e26b65d
Fix small windows opening on bracket update, fix other problematic br…
joaorb64 Jun 13, 2023
56a8c9d
Update locale files
actions-user Jun 13, 2023
5e21985
Update exe
actions-user Jun 13, 2023
89b5156
Fix bracket when there are byes in the middle of the seeding map
joaorb64 Jun 13, 2023
9b0ea13
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Jun 13, 2023
4387a53
Update exe
actions-user Jun 13, 2023
4639644
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Jun 13, 2023
ec6c713
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Jun 13, 2023
45aac63
Update exe
actions-user Jun 13, 2023
830f0ec
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Jun 16, 2023
dd16e3d
Update exe
actions-user Jun 16, 2023
55c7ee1
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Jun 20, 2023
1770ef6
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Jun 20, 2023
43b3ee0
Update exe
actions-user Jun 20, 2023
ef89292
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Jun 20, 2023
6984653
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Jun 20, 2023
afbdfa5
Fix seed import
joaorb64 Jun 20, 2023
6a10d21
Update exe
actions-user Jun 21, 2023
b27fe34
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Jun 22, 2023
30571ee
Update locale files
actions-user Jun 22, 2023
f154038
Update exe
actions-user Jun 22, 2023
d73bfab
Fix exporting
joaorb64 Jun 22, 2023
16f0d4e
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Jun 22, 2023
b13efae
Update exe
actions-user Jun 22, 2023
81158d1
Updates for latest tests
joaorb64 Jun 23, 2023
0f318d7
Update exe
actions-user Jun 23, 2023
cbee572
More fixes
joaorb64 Jun 24, 2023
0ccdfb2
Update locale files
actions-user Jun 24, 2023
9a98824
Update exe
actions-user Jun 24, 2023
386c41e
More fixes
joaorb64 Jun 28, 2023
9d0a32e
Update exe
actions-user Jun 28, 2023
b5cb957
More fixes
joaorb64 Jun 29, 2023
44899aa
Rebase main
joaorb64 Jun 29, 2023
9cb37bb
Update locale files
actions-user Jun 29, 2023
edbd6d2
Update exe
actions-user Jun 29, 2023
f02666c
Merge branch 'main' of github.com:joaorb64/TournamentStreamHelper int…
joaorb64 Aug 2, 2023
93029de
Update locale files
actions-user Aug 2, 2023
1d2d974
Update exe
actions-user Aug 2, 2023
a49f7c9
Rebase main
joaorb64 Aug 24, 2023
e680e01
Merge branch 'feature/fix-brackets' of github.com:joaorb64/Tournament…
joaorb64 Aug 24, 2023
441ac92
Update locale files
actions-user Aug 24, 2023
9726c6d
Update exe
actions-user Aug 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified TSH.exe
Binary file not shown.
2 changes: 1 addition & 1 deletion layout/bracket_ultimate/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
141 changes: 93 additions & 48 deletions src/TSHBracket.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .Helpers.TSHLocaleHelper import TSHLocaleHelper
from loguru import logger


class BracketSet():
BYE = -1
PENDING = -2
Expand All @@ -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)

Expand All @@ -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

Expand All @@ -82,42 +94,46 @@ 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

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):
Expand All @@ -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
Expand All @@ -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):
Expand All @@ -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]
Expand All @@ -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:
Expand Down Expand Up @@ -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
Expand All @@ -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))
return TSHLocaleHelper.matchNames.get("losers_round").format(abs(roundNumber))
Loading