Skip to content

Instantly share code, notes, and snippets.

@Prof9
Last active March 18, 2023 23:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Prof9/f7bf07b5255bd07768f1 to your computer and use it in GitHub Desktop.
Save Prof9/f7bf07b5255bd07768f1 to your computer and use it in GitHub Desktop.
Mega Man Battle Network 4 Tournament Generator
from enum import Enum
class Object(object):
pass
class Version(Enum):
RedSun = 0
BlueMoon = 1
class Navi(Enum):
Empty = -1
GutsMan = 0
FireMan = 1
NumberMan = 2
AquaMan = 3
Roll = 4
WindMan = 5
MetalMan = 6
WoodMan = 7
ThunderMan = 8
SearchMan = 9
ProtoMan = 10
JunkMan = 11
SparkMan = 12
TopMan = 13
ColdMan = 14
BurnerMan = 15
VideoMan = 16
KendoMan = 17
Ponta1 = 20
Tetsu1 = 21
Ponta2 = 22
Tetsu2 = 23
Flave1 = 24
Riki = 25
Flave2 = 26
Paulie1 = 27
JackBomber = 28
Paulie2 = 29
MegaMan = 30
class Rng(object):
def __init__(self):
self.seed = 0xA338244F
def print_seed(self):
print(format(self.seed, "08X"))
def next(self):
self.seed = (((self.seed << 1) + (self.seed >> 31) + 1) ^ 0x873CA9E5) & 0xFFFFFFFF
return self.seed
def nextInt(self, max):
return self.next() % max
class TournamentGenerator(object):
def __init__(self, version):
self.allSoulNavis = [
[
[Navi.GutsMan, Navi.FireMan],
[Navi.Roll, Navi.WindMan],
[Navi.ThunderMan, Navi.SearchMan]
], [
[Navi.AquaMan, Navi.NumberMan],
[Navi.MetalMan, Navi.WoodMan],
[Navi.ProtoMan, Navi.JunkMan]
]
]
self.allUniqueNavis = [
[Navi.SparkMan, Navi.TopMan],
[Navi.VideoMan, Navi.BurnerMan],
[Navi.KendoMan, Navi.ColdMan]
]
self.allOtherNavis = [
[
[Navi.NumberMan, Navi.AquaMan],
[Navi.MetalMan, Navi.WoodMan],
[Navi.ProtoMan, Navi.JunkMan]
], [
[Navi.GutsMan, Navi.FireMan],
[Navi.Roll, Navi.WindMan],
[Navi.ThunderMan, Navi.SearchMan]
]
]
self.rng = Rng()
self.version = version
self.game = 0
self.tourney = 0
self.soulSwapped = False
self.soulNavis = [
[Navi.Empty, Navi.Empty],
[Navi.Empty, Navi.Empty],
[Navi.Empty, Navi.Empty]
]
self.uniqueNavis = [
[Navi.Empty, Navi.Empty, Navi.Empty, Navi.Empty],
[Navi.Empty, Navi.Empty, Navi.Empty, Navi.Empty],
[Navi.Empty, Navi.Empty, Navi.Empty, Navi.Empty]
]
self.normalNavis = [
[Navi.Ponta1, Navi.Ponta2],
[Navi.Flave1, Navi.Flave2],
[Navi.JackBomber]
]
self.heelNavis = [
[Navi.Tetsu1, Navi.Tetsu2],
[Navi.Riki],
[Navi.Paulie1, Navi.Paulie2]
]
self.doneNavis = []
self.matches = [Navi.Empty, Navi.Empty, Navi.Empty]
def addSoulNavis(self):
if self.game == 0:
if self.tourney == 0 or self.rng.nextInt(2) == 0:
self.soulNavis[self.tourney] = self.allSoulNavis[self.version.value][self.tourney]
else:
self.soulNavis[self.tourney] = self.allSoulNavis[self.version.value][self.tourney][::-1]
# TODO: Can be True/False randomly (50%/50%)
elif self.tourney == 2 or (self.tourney == 1 and False):
if not self.soulSwapped:
self.soulSwapped = True
self.soulNavis[self.tourney] = self.soulNavis[self.tourney][::-1]
def addUniqueNavis(self):
navis = self.allUniqueNavis[self.tourney]
# Run at least once
for i in range(0, max(self.rng.nextInt(4), 1)):
a = self.rng.nextInt(8) % len(navis)
# Actually what the game does.
# Not a typo.
b = (a + 1) & (len(navis) - 1)
navis[a], navis[b] = navis[b], navis[a]
done = []
notDone = []
for navi in navis:
if navi in self.doneNavis:
done.append(navi)
else:
notDone.append(navi)
for navi in notDone:
if navi in self.allOtherNavis[self.version.value][self.tourney]:
first = navi
break
else:
if notDone:
first = notDone[0]
else:
first = done[0]
# Check if chosen unique Navi is same as last time.
if first == self.uniqueNavis[self.tourney][0]:
first = done[1]
last = self.uniqueNavis[self.tourney][0]
if last == Navi.Empty:
last = notDone[1]
elif last in navis:
last = last
elif False:
print("Check this weird edge case");
# what is this?
last = notDone[1]
else:
print("Check this weird edge case");
# huh?
last = Navi.GutsMan
self.uniqueNavis[self.tourney][0] = first
self.uniqueNavis[self.tourney][len(navis) - 1] = last
i = 1
for navi in navis:
if navi != first and navi != last:
self.uniqueNavis[self.tourney][i] = navi
i += 1
if not first in self.doneNavis:
self.doneNavis.append(first)
def fillBracket(self):
soulNavi = self.soulNavis[self.tourney][0]
uniqueNavi = self.uniqueNavis[self.tourney][0]
normalNavi = self.normalNavis[self.tourney][0]
heelNavi = self.heelNavis[self.tourney][0]
bracketL = [Navi.MegaMan]
bracketR = []
if self.game == 0 and self.tourney == 0:
if self.rng.nextInt(2) == 0:
bracketL.append(heelNavi)
bracketL.append(soulNavi)
bracketR.append(uniqueNavi)
else:
bracketL.append(normalNavi)
bracketL.append(soulNavi)
bracketR.append(uniqueNavi)
else:
ra, rb = self.rng.nextInt(4), self.rng.nextInt(2)
if ra == 0 and rb == 0:
bracketL.append(uniqueNavi)
bracketL.append(normalNavi)
bracketR.append(soulNavi)
bracketR.append(heelNavi)
elif ra == 0 and rb == 1:
bracketL.append(uniqueNavi)
bracketL.append(heelNavi)
bracketR.append(soulNavi)
bracketR.append(normalNavi)
elif ra == 1 and rb == 0:
bracketL.append(soulNavi)
bracketL.append(normalNavi)
bracketR.append(uniqueNavi)
bracketR.append(heelNavi)
elif ra == 1 and rb == 1:
bracketL.append(soulNavi)
bracketL.append(heelNavi)
bracketR.append(uniqueNavi)
bracketR.append(normalNavi)
elif ra == 2 and rb == 0:
bracketL.append(normalNavi)
bracketL.append(uniqueNavi)
bracketR.append(soulNavi)
elif ra == 2 and rb == 1:
bracketL.append(normalNavi)
bracketL.append(soulNavi)
bracketR.append(uniqueNavi)
elif ra == 3 and rb == 0:
bracketL.append(heelNavi)
bracketL.append(uniqueNavi)
bracketR.append(soulNavi)
elif ra == 3 and rb == 1:
bracketL.append(heelNavi)
bracketL.append(soulNavi)
bracketR.append(uniqueNavi)
loseNavis = []
loseNavis.append(self.soulNavis[self.tourney][1])
for navi in reversed(self.uniqueNavis[self.tourney]):
if navi != Navi.Empty:
loseNavis.append(navi)
break
for navi in reversed(self.normalNavis[self.tourney]):
if navi != Navi.Empty:
if navi not in (bracketL + bracketR):
loseNavis.append(navi)
break
for navi in reversed(self.heelNavis[self.tourney]):
if navi != Navi.Empty:
if navi not in (bracketL + bracketR):
loseNavis.append(navi)
break
# This should be len(loseNavis) - 1, but they forgot to push that onto the stack
# Put a random lose Navi in the final left bracket slot
prev = self.rng.seed >> 31
r = self.rng.nextInt(8)
while (r > prev):
r -= 1 + prev
bracketL.append(loseNavis[r])
# Append unfinished right bracket to finished left bracket
bracket = bracketL + bracketR
# Gather all leftover Navis
allNavis = []
allNavis += self.soulNavis[self.tourney]
allNavis += self.uniqueNavis[self.tourney]
allNavis += self.normalNavis[self.tourney]
allNavis += self.heelNavis[self.tourney]
leftNavis = []
for navi in allNavis:
if navi != Navi.Empty and navi not in bracket:
leftNavis.append(navi)
# Shuffle leftover Navis
for i in range(5):
ra = self.rng.nextInt(8) % len(leftNavis)
rb = (ra + 1) % len(leftNavis)
leftNavis[ra], leftNavis[rb] = leftNavis[rb], leftNavis[ra]
# Fill rest of bracket with leftover Navis
r = self.rng.nextInt(8) % len(leftNavis)
while (len(bracket) < 8):
bracket.append(leftNavis[r])
r = (r + 1) % len(leftNavis)
# Flip matchups
r = self.rng.next()
for i in range(0, 8, 2):
if r & 1:
bracket[i], bracket[i + 1] = bracket[i + 1], bracket[i]
r >>= 4
# Flip semifinals
if (self.rng.nextInt(2) == 0):
bracket[0], bracket[2] = bracket[2], bracket[0]
bracket[1], bracket[3] = bracket[3], bracket[1]
bracket[4], bracket[6] = bracket[6], bracket[4]
bracket[5], bracket[7] = bracket[7], bracket[5]
# Flip finals
if (self.rng.nextInt(2) == 0):
bracket[0], bracket[4] = bracket[4], bracket[0]
bracket[1], bracket[5] = bracket[5], bracket[1]
bracket[2], bracket[6] = bracket[6], bracket[2]
bracket[3], bracket[7] = bracket[7], bracket[3]
for i in range(8):
navi = bracket[i]
if navi == Navi.MegaMan:
priority = 0
elif navi in self.soulNavis[self.tourney]:
priority = self.soulNavis[self.tourney].index(navi)
elif navi in self.uniqueNavis[self.tourney]:
priority = self.uniqueNavis[self.tourney].index(navi)
elif navi in self.normalNavis[self.tourney]:
priority = self.normalNavis[self.tourney].index(navi)
elif navi in self.heelNavis[self.tourney]:
priority = self.heelNavis[self.tourney].index(navi)
self.bracket[i] = Object()
self.bracket[i].navi = navi
self.bracket[i].priority = priority
def getMatches(self):
self.matches[0] = self.getMatch(self.bracket)
round2 = self.advanceRound(self.bracket)
self.matches[1] = self.getMatch(round2)
round3 = self.advanceRound(round2)
self.matches[2] = self.getMatch(round3)
return self.matches
def advanceRound(self, round):
next = []
for i in range(len(round) >> 1):
left = round[i * 2]
right = round[i * 2 + 1]
next.append(self.matchResult(left, right))
return next
def getMatch(self, round):
for i in range(0, len(round), 2):
if round[i].navi == Navi.MegaMan:
return round[i + 1].navi
elif round[i + 1].navi == Navi.MegaMan:
return round[i].navi
def matchResult(self, left, right):
if left.navi == Navi.MegaMan:
return left
elif right.navi == Navi.MegaMan:
return right
if left.priority < right.priority:
return left
elif right.priority < left.priority:
return right
if left.navi.value < 20 and right.navi.value >= 20:
return left
elif right.navi.value < 20 and left.navi.value >= 20:
return right
if self.rng.nextInt(2) == 0:
return left
else:
return right
def create(self):
self.bracket = [[Navi.Empty, 0]] * 8
self.addSoulNavis()
self.addUniqueNavis()
self.fillBracket()
self.tourney += 1
if (self.tourney == 3):
self.game += 1
self.tourney = 0
return self.getMatches()
def padNaviName(self, name):
r = name
if r[-1] in "12":
r = name[:-1]
if len(r) < 8:
r += "\t"
return r
def bracket_to_string(self):
s = ""
for entry in self.bracket:
s += "\t" + self.padNaviName(entry.navi.name)
return s[1:]
def matches_to_string(self):
s = ""
for navi in self.matches:
s += "\t" + self.padNaviName(navi.name)
return s[1:]
rng = Rng()
for i in range(10000):
tg = TournamentGenerator(Version.BlueMoon)
tg.rng.seed = rng.seed
s = "frame " + str(i).rjust(4) + "\t"
s += "seed " + format(tg.rng.seed, "08X")
if (True):
tg.create()
s += "\n\t1:\t" + tg.bracket_to_string()
tg.create()
s += "\n\t2:\t" + tg.bracket_to_string()
tg.create()
s += "\n\t3:\t" + tg.bracket_to_string()
else:
tg.create()
s += "\t" + tg.matches_to_string()
tg.create()
s += "\t" + tg.matches_to_string()
tg.create()
s += "\t" + tg.matches_to_string()
rng.next()
print(s)
@MavZX12
Copy link

MavZX12 commented Mar 18, 2023

Is there a patch for this tournament generator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment