鸣潮小团快跑(B组首轮)

昨天是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}%)")
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值