昨天是CN团子的至暗时刻,最厉害的椿和小土豆双双被淘汰(痛哭)牢卡拿到二连胜,很难不让人怀疑牢卡是不是偷偷送牛奶给赛事方。
今天也是更新了B组首轮的模拟预测结果和预测代码。
模拟10万场比赛,获胜概率相差不大。
洛可可 获胜 18956次(18.956%)
布兰特 获胜 18985次(18.985%)
坎特蕾拉 获胜 13527次(13.527%)
赞妮 获胜 18000次(18.0%)
卡提希娅 获胜 11530次(11.53%)
菲比 获胜 19002次(19.002%)
import random
BOARD_SIZE = 24
DICE_MAX = 3
class Tuanzi:
def __init__(self, name):
self.name = name
self.position = 0
self.force_last = False # 用于长离技能
self.kati = False
self.zanni = False
self.kanteleila = True
self.bulante = False
self.luokeke = False
def __repr__(self):
return self.name
class Board:
def __init__(self):
self.grid = [[] for _ in range(BOARD_SIZE)]
def place(self, tuanzi, pos):
self.grid[pos].append(tuanzi)
tuanzi.position = pos
def remove(self, tuanzi):
self.grid[tuanzi.position].remove(tuanzi)
def move_stack(self, tuanzi, steps):
pos = tuanzi.position
stack = self.grid[pos]
index = stack.index(tuanzi)
moving = stack[index:]
self.grid[pos] = stack[:index]
new_pos = min(pos + steps, BOARD_SIZE - 1)
self.grid[new_pos].extend(moving)
for t in moving:
t.position = new_pos
def get_rankings(self, tuanzis):
ranking = []
# 按 position 逆序排序
sorted_tuanzis = sorted(tuanzis, key=lambda x: x.position, reverse=True)
for t in sorted_tuanzis:
if t in ranking: continue
for tuanzi in reversed(self.grid[t.position]):
ranking.append(tuanzi)
return ranking
def is_finished(self):
return any(tuanzi.position >= BOARD_SIZE - 1 for stack in self.grid for tuanzi in stack)
def apply_skill(tuanzi, board, rankings):
name = tuanzi.name
bonus = 0
pos = tuanzi.position
stack = board.grid[pos]
dice = random.randint(1, DICE_MAX)
# 卡卡罗:开始移动时如果在最后一名则额外前进3格
if name == "卡卡罗":
if rankings[-1] == tuanzi:
bonus += 3
# 珂莱塔:28%概率以骰子的步数前进两次
elif name == "珂莱塔":
if random.random() < 0.28:
bonus += dice
# 长离:如果下方堆叠其他团子,下一回合有65%概率最后一个行动
elif name == "长离":
if stack.index(tuanzi) > 0 and random.random() < 0.65:
tuanzi.force_last = True
# 今汐:如果头顶堆叠其他团子,有40%概率移动到所有团子的最上方
elif name == "今汐":
if stack.index(tuanzi) < len(stack) - 1 and random.random() < 0.4:
upper = stack[stack.index(tuanzi):]
board.grid[pos] = stack[:stack.index(tuanzi)]
for t in upper:
board.grid[pos].append(t)
# 椿:自身行动时50%概率触发。当前格子除了自己外每有一个团子,自身行动格数+1,且不会带其他团子一起移动
elif name == "椿":
if random.random() < 0.5:
extra = len(stack) - 1
board.remove(tuanzi)
new_pos = min(pos + dice + extra, BOARD_SIZE - 1)
board.grid[new_pos].append(tuanzi)
tuanzi.position = new_pos
return 0
# 守岸人:骰子只会抛出2或3
elif name == "守岸人":
dice = random.choice([2, 3])
# 如果是最后一个移动,额外前进两格
elif name == "洛可可":
if tuanzi.luokeke == True:
bonus = 2
tuanzi.luokeke = False
# 如果是第一个移动,额外前进两格
elif name == "布兰特":
if tuanzi.bulante == True:
bonus = 2
tuanzi.bulante = False
# 移动过程中首次遇到团子时,会和此格子所有团子堆叠,在本回合一起移动,每次比赛最多触发一次
elif name == "坎特蕾拉":
if tuanzi.kanteleila == True:
end_pos = min(pos + dice, BOARD_SIZE - 1)
if len(board.grid[pos]) > 1:
board.grid[pos] = []
board.grid[end_pos].extend(stack)
for t in stack:
t.position = end_pos
tuanzi.kanteleila = False
else:
for p in range(pos + 1, end_pos + 1):
if len(board.grid[p]) > 0:
board.move_stack(tuanzi, p - pos)
new_stack = board.grid[p]
board.grid[p] = []
board.grid[end_pos].extend(new_stack)
for t in new_stack:
t.position = end_pos
tuanzi.kanteleila = False
break
if tuanzi.kanteleila == False: return 0
# 只会掷出1或3。开始移动时,如果处于堆叠状态,下回合有40%概率额外前进两格
elif name == "赞妮":
dice = random.choice([1, 3])
if tuanzi.zanni == True and random.random() < 0.4:
bonus = 2
tuanzi.zanni = False
if len(stack) > 1: tuanzi.zanni = True
# 每场比赛最多触发一次。自身移动结束后,若处于最后一名,本场比赛剩余回合都会60%概率额外前进两格
elif name == "卡提希娅":
if tuanzi.kati == True:
if random.random() < 0.6: bonus = 2
# 50%概率额外前进一格
elif name == "菲比":
if random.random() < 0.5:
bonus += 1
return dice + bonus
def step_start_act(order):
if order[0].name == "布兰特":
order[0].bulante = True
if order[-1].name == "洛可可":
order[-1].luokeke = True
def step_end_act(tuanzi, rankings):
# 判断卡提希娅技能是否触发
if tuanzi.name == '卡提希娅' and tuanzi.kati == False:
if rankings[-1].name == '卡提希娅':
tuanzi.kati == True
START_POSITIONS_A = {
"卡卡罗": 0,
"珂莱塔": -1,
"长离": -1,
"今汐": -2,
"椿": -2,
"守岸人": -3
}
START_POSITIONS_B = {
"洛可可": 1,
"布兰特": 1,
"坎特蕾拉": 1,
"赞妮": 1,
"卡提希娅": 1,
"菲比": 1
}
def main():
# names = ["卡卡罗", "长离", "珂莱塔", "椿", "今汐", "守岸人"]
names = ["洛可可", "布兰特", "坎特蕾拉", "赞妮", "卡提希娅", "菲比"]
# 小组赛首轮随机决定出场顺序
random.shuffle(names)
tuanzis = [Tuanzi(name) for name in names]
board = Board()
for t in tuanzis:
board.place(t, START_POSITIONS_B[t.name])
# print("初始排名:", [(t.name, t.position) for t in board.get_rankings(tuanzis)])
round_num = 1
while not board.is_finished():
# print(f"\n--- 第 {round_num} 回合 ---")
order = tuanzis[:]
# 优先将被标记为“force_last”的长离放到最后
force_last = [t for t in order if t.force_last == True]
for t in force_last:
t.force_last = False
order = [t for t in order if t not in force_last]
if round_num != 1:
random.shuffle(order)
else: order.reverse()
order.extend(force_last)
for t in order:
if t not in board.grid[t.position]:
continue
# 开始移动时的排名
rankings = board.get_rankings(tuanzis)
step_start_act(order)
steps = apply_skill(t, board, rankings)
board.move_stack(t, steps)
# 结束移动时的排名
rankings = board.get_rankings(tuanzis)
step_end_act(t, rankings)
# print(f"{t.name} 前进 {steps} 格 -> 位置 {t.position}")
# print("当前排名:", [(t.name, t.position) for t in board.get_rankings(order)])
round_num += 1
winner = board.get_rankings(order)[0]
# print(f"\n比赛结束!胜利者是:{winner.name}")
return winner
if __name__ == "__main__":
result_A = {
"卡卡罗": 0,
"珂莱塔": 0,
"长离": 0,
"今汐": 0,
"椿": 0,
"守岸人": 0
}
result_B = {
"洛可可": 0,
"布兰特": 0,
"坎特蕾拉": 0,
"赞妮": 0,
"卡提希娅": 0,
"菲比": 0
}
from tqdm import tqdm
iters = 100000
for i in tqdm(range(iters)):
result_B[main().name] += 1
for k in result_B:
print(f"{k} 获胜 {result_B[k]}次({100 * result_B[k] / iters}%)")