You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

输入上一轮比分后赛事Bracket未自动更新晋级队伍

问题:赛事对阵Bracket无法自动晋级获胜队伍

我正在学习Svelte,想要实现一个能自动选出下一轮晋级队伍的赛事对阵Bracket。当前遇到的问题是:输入初始轮次(如temp8得2分)的比分后,获胜队伍并未自动进入下一轮(本该对阵temp1)。我尝试过在createMatches函数递归时传递bracket副本,以及在Svelte代码中传递副本,但都无效,我认为函数逻辑本身是正确的,恳请提供解决建议或方向。

问题截图:
问题截图

Svelte组件代码

<script lang="ts">
  import { createMatches } from "$lib/handlers/bracketupdate";
  import type { Bracket } from "$lib/types/bracket";
  export let bracket: Bracket;
  export let readonly = false;

  $: bracket = createMatches(bracket);
</script>

<div class="bracket">
  {#each Object.keys(bracket.scores)
    .sort((a, b) => +a - +b)
    .map((k) => +k) as round}
    <div class="round">
      {#each bracket.scores[round] as match}
        <div class="match">
          <div class="team">
            <p>{match.team1._id}</p>
            <input type="number" bind:value={match.team1_score} {readonly} />
          </div>
          <div class="team">
            <p>{match.team2._id}</p>
            <input type="number" bind:value={match.team2_score} {readonly} />
          </div>
        </div>
      {/each}
    </div>
  {/each}
</div>

Bracket生成函数代码

import { bye, type Bracket, type BracketMatch, pending } from "$lib/types/bracket";
import type { Team } from "$lib/types/teams";

export function createMatches(bracket: Bracket, round = 0): Bracket {
  // Base case
  if (bracket.teams.length < 2) return bracket;

  if (round === 0) {
    // Add byes to make the number of teams a power of 2
    const numByes = 2 ** Math.ceil(Math.log2(bracket.teams.length)) - bracket.teams.length;
    if (numByes > 0) {
      for (let i = 0; i < numByes; i++) {
        bracket.teams.push(bye);
      }
    }

    // Shuffle the teams by seed
    bracket.teams = shuffle(bracket.teams);
  }

  // Create matches for this round
  const matches: BracketMatch[] = [];
  for (let i = 0; i < bracket.teams.length; i += 2) {
    const team1 = bracket.teams[i];
    const team2 = bracket.teams[i + 1];

    matches.push({ team1, team2, team1_score: 0, team2_score: 0 });
  }

  // Add the matches to the bracket
  bracket.scores[round] = matches;

  // Create the next round
  const winners = getWinners(matches);
  bracket.teams = winners;
  createMatches(bracket, round + 1);

  return bracket;
}

function getWinners(matches: BracketMatch[]) {
  return matches.map((match) => {
    if (match.team1._id === bye._id) return match.team2;
    if (match.team2._id === bye._id) return match.team1;

    if (match.team1_score > match.team2_score) return match.team1;
    if (match.team2_score > match.team1_score) return match.team2;

    return pending;
  });
}

function shuffle(teams: Team[]) {
  // Create a copy of the teams array
  let result = [...teams];

  // Calculate and loop for the number of rounds
  const numRounds = Math.log2(teams.length / 2);
  for (let i = 0; i < numRounds; i++) {
    // Create a new round
    const round = [];

    // Calculate the number of teams to splice
    const splice = Math.pow(2, i);

    // Loop while there are still teams to splice
    while (result.length) {
      round.push(...result.splice(0, splice));
      round.push(...result.splice(-splice));
    }

    // Set the round to the result
    result = round;
  }

  return result;
}

解决建议

1. 核心问题:每次比分变化都会重置整个Bracket

你在组件里用$: bracket = createMatches(bracket),意味着只要bracket有任何变化(比如输入比分),就会重新执行createMatches。但这个函数的逻辑是从头构建整个Bracket:

  • round===0时会重新洗牌队伍
  • 所有新生成的match都会被设置team1_score:0, team2_score:0,直接覆盖你输入的比分
  • 后续轮次的队伍会被重新计算,但因为比分被重置,结果自然不对

2. 修复方案:拆分初始化与更新逻辑

把原有createMatches拆成两个独立函数,分别负责初始化结构和更新晋级队伍:

(1)初始化函数:只执行一次

负责生成完整的Bracket结构,后续轮次默认填充pending队伍,不覆盖比分:

export function initializeBracket(initialTeams: Team[]): Bracket {
  const bracket: Bracket = { 
    teams: [...initialTeams], 
    scores: {} 
  };

  // 添加轮空队伍,凑成2的幂次
  const numByes = 2 ** Math.ceil(Math.log2(bracket.teams.length)) - bracket.teams.length;
  if (numByes > 0) {
    for (let i = 0; i < numByes; i++) {
      bracket.teams.push(bye);
    }
  }

  // 洗牌队伍
  bracket.teams = shuffle(bracket.teams);

  // 递归生成所有轮次结构
  function buildRounds(currentBracket: Bracket, round: number) {
    if (currentBracket.teams.length < 2) return currentBracket;

    // 生成当前轮次的match
    const matches = [];
    for (let i = 0; i < currentBracket.teams.length; i += 2) {
      matches.push({
        team1: currentBracket.teams[i],
        team2: currentBracket.teams[i + 1],
        team1_score: 0,
        team2_score: 0
      });
    }
    currentBracket.scores[round] = matches;

    // 下一轮默认填充pending,等待后续更新
    currentBracket.teams = matches.map(() => pending);
    buildRounds(currentBracket, round + 1);

    return currentBracket;
  }

  return buildRounds(bracket, 0);
}

(2)更新晋级函数:比分变化时执行

只根据当前已有的比分,从第一轮开始依次计算后续轮次的晋级队伍,保留已有比分:

export function updateBracketWinners(bracket: Bracket): Bracket {
  // 从第一轮开始,逐层计算胜者
  let currentWinners = getWinners(bracket.scores[0]);

  // 更新后续每一轮的队伍
  for (let round = 1; bracket.scores[round]; round++) {
    // 保留原有比分,只更新对阵队伍
    bracket.scores[round] = currentWinners.reduce((acc, _, idx, arr) => {
      if (idx % 2 === 0) {
        const existingMatch = bracket.scores[round][idx / 2];
        acc.push({
          ...existingMatch,
          team1: arr[idx],
          team2: arr[idx + 1]
        });
      }
      return acc;
    }, [] as BracketMatch[]);

    // 计算当前轮的胜者,用于下一轮
    currentWinners = getWinners(bracket.scores[round]);
  }

  // 返回新对象触发Svelte响应式更新
  return { ...bracket };
}

3. 组件中调整逻辑

初始化时调用一次initializeBracket,之后只在比分变化时触发更新:

<script lang="ts">
  import { initializeBracket, updateBracketWinners } from "$lib/handlers/bracketupdate";
  import type { Bracket } from "$lib/types/bracket";
  import type { Team } from "$lib/types/teams";
  
  export let initialTeams: Team[];
  export let readonly = false;

  // 初始化Bracket结构
  let bracket: Bracket = initializeBracket(initialTeams);

  // 比分变化时更新晋级队伍
  $: {
    // 依赖bracket.scores触发更新,Svelte会自动监听内部属性变化
    bracket = updateBracketWinners({ ...bracket });
  }
</script>

4. 额外注意:Svelte响应式触发

Svelte需要检测到引用变化才会更新DOM,所以更新函数必须返回新的对象/数组,而不是直接修改原对象。比如用{ ...bracket }创建副本,或者修改数组时使用[...arr]。


内容的提问来源于stack exchange,提问作者mhay10

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:55:01