竞技场:AI世界杯 ⚽🏆

Chaotic Crossing Clash —— 用代码指挥 AI Agent,进球比对手更多!
扣哒世界 多人竞技场 | 2v2 足球 · 球员 + 教练 + 守门员

1. 竞技场简介

Intro to the AI World Cup Arena

欢迎来到绿茵赛场!AI世界杯是一款 2 对 2 的竞技足球编程游戏。它最特别的地方在于:场上奔跑的球员、场边指挥的教练,全都不是普通角色,而是一个个由你的代码"注入灵魂"的 AI Agent(智能体)

你可以把 AI Agent 理解为一个"会自己思考、自己行动"的程序小人。你不需要用鼠标一步步操控它,而是提前写好一套决策逻辑(例如"如果我有球就射门,否则就去追球"),然后 AI Agent 就会在比赛中不停地按照这套逻辑自动做出判断和动作。你的代码写得越聪明,你的 AI Agent 踢得就越好——这正是"AI世界杯"这个名字的由来。

1.1 你要指挥的三类 AI Agent(Three Agents)

在这场比赛里,你的代码将同时操控三个 AI Agent。它们分工不同,请先记住它们各自的角色:

一句话理解:你既是球队的程序员,也是场边的教练。两名球员 AI Agent 各自为战(多线程并行),教练 AI Agent 统筹全局(主线程),而守门员是一个需要你去攻破的固定 NPC。

1.2 编程语言(Languages)

本竞技场支持用 PythonC++ 两种语言来编写 AI Agent 的逻辑。两种语言的游戏思路完全一致——都是"为球员写线程处理函数,为教练写主循环",只是语法写法不同。

本篇文档中的每一段示例代码,都会同时给出 Python 和 C++ 两个版本,你可以点击代码框上方的语言标签来切换查看。初学者建议先从 Python 入手,它的写法更接近自然语言、更容易读懂;熟悉之后再尝试 C++ 也会很轻松。

红队球门
蓝队球门
中线开球点
图1:球场示意图——红队防守左门,蓝队防守右门,中央为开球点

2. 游戏目标与胜负

Game Objective — Score to Win

目标非常简单,和真实足球一样:把球打进对方球门,让自己的进球数比对手多。下面我们把"比赛怎么进行""怎么算赢"一条条讲清楚。

2.1 比赛流程(Two Rounds)

一场比赛分为两个回合(就像足球的上下半场),每个回合时长为 params.halfTime 秒。两个回合分别编号为第 0 回合和第 1 回合(程序里习惯从 0 开始数):

之所以让两队轮流开球,是为了公平——开球方会先碰到球,稍占一点便宜,两回合各开一次就抵消了。

2.2 胜负判定规则(Win Conditions)

当第二回合结束的那一刻,系统会立即按下面的顺序判定谁赢:

优先级判定标准
① 首先看进球数高者获胜。这是最直接的胜负标准。
② 若进球数相同粉丝支持度更高的队伍获胜(支持度是什么,见下一节)。
③ 若支持度也相同掷硬币决定——但这个"硬币"是基于种子(seed)的,同一场比赛无论重播多少次,结果都完全一样。
确定性设计(重要观念):这个游戏没有任何真正的随机性——连"掷硬币"都是固定结果。这意味着:相同的代码对阵相同的对手,比赛过程和结果永远一模一样。所以你可以放心地反复测试、优化你的 AI Agent,不用担心"这次是运气好"。

2.3 粉丝支持度(Fan Support)

观众来看球,最爱看的是积极进攻的足球。所以游戏设计了一个"粉丝支持度"机制来奖励主动进攻的一方。比赛过程中,双方都会不断积累粉丝支持度,它会直接显示为你教练 AI Agent(英雄)的生命值数字——数字越高,说明你的球队踢得越受欢迎。

以下行为会为你积累支持度:

行为支持度奖励
成功把球传给自己的队友params.activity.pass
赢得一次抢断对抗(clash)params.activity.clash
射向对方球门(无论被扑出还是进球)params.activity.shot
把球留在对方半场里每秒 params.activity.ballInOpponentHalfPerSecond
攻势至上:僵局(deadlock)和消极打法,永远会输给积极、有建设性的代码。哪怕两队进球数打平,主动传球、频繁射门、把球压在对方半场的一方,也会靠支持度笑到最后。所以写代码时,要鼓励你的 AI Agent 大胆进攻,而不是缩在自家门前干等。

3. 球员 AI Agent 控制

Mate Control — Your Two Field Agents

你控制两名球员 AI Agent。前面说过,每名球员都在自己独立的线程里运行你的处理函数。比赛开始时,这个处理函数会为每名球员各自调用一次;一旦调用,函数里的循环就会一直跑到比赛结束(球员永远不会死亡,也不会重生),所以你通常会在函数里写一个 while 无限循环,让球员不停地思考和行动。

3.1 启动球员线程(Spawn Handler)

要让球员 AI Agent 动起来,你必须先"注册"一个处理函数,告诉系统:"每当一名球员登场,就用这个函数来控制它。"注册的方式是调用 hero.on("spawn-mate", 函数名)

新手最常犯的错:忘记写 hero.on("spawn-mate", ...)!如果没有这一行,球员线程根本不会启动,你会看到两名球员站在原地一动不动。请务必记得加上它。

下面是一段最基础的球员逻辑:"有球就射门,没球就追球。"

def onSpawnMate(mate):
    while True:
        if mate.hasBall:
            mate.shoot()        # 有球就射门
        else:
            mate.chaseBall()    # 没球就追球

hero.on("spawn-mate", onSpawnMate)
auto onSpawnMate(auto mate) {
    while (true) {
        if (mate.hasBall) {
            mate.shoot();       // 有球就射门
        } else {
            mate.chaseBall();   // 没球就追球
        }
    }
}

int main() {
    hero.on("spawn-mate", onSpawnMate);
    return 0;
}
分工小技巧:两名球员会共用同一个处理函数,但你可以用 mate.mateId(值为 01)来区分它们,从而分配不同角色。例如:让 0 号一直往前压当"前锋",让 1 号留在后场当"后卫",这样球队才有层次,不会两个人挤在一起抢同一个球。

3.2 基础指令(Move Commands)

球员 AI Agent 的移动和测距,靠下面两个基础指令:

指令说明
mate.moveTo(x, y)朝目标点 (x, y) 移动一步(步长为 params.stepDistance)后就返回。注意:它一次只走一小步!这一步期间线程会被阻塞(暂停),走完这步之后你的代码会重新执行、重新做决策。所以要走到远处,通常要放在循环里反复调用。
mate.distanceTo(target)返回本球员到任意带有 xy 坐标的对象(比如球、对手、球门)之间的距离。常用来判断"够不够得着"。
速度要点(很关键):持球的球员会变慢——速度会乘以 params.carrierSpeedFactor(默认 0.75,即降到 75%)。这带来一个重要结论:空手的追赶者总能追上带球的盘带者。所以别指望一个人一路带球过全场,那一定会被抢断——正确做法是靠传球把球快速推进。另外,被眩晕的球员无法移动,此时调用 moveTo 会一直卡住,直到眩晕结束才返回。

3.3 抢断能力 clash(The One Mate Ability)

mate.clash() 是球员 AI Agent 唯一的主动技能:从敌方持球者脚下把球抢过来。它的冷却时间是 params.clashCooldown 秒,而且每次尝试都会消耗冷却——也就是说,无论你抢成功还是失败,都得等冷却结束才能再抢一次。

结果触发条件与效果
✅ 成功当敌方持球者在 params.clashRange 范围内、且不处于免疫状态时:球立刻转移到你脚下,对手被眩晕 params.clashStunTarget 秒,同时你还获得一小段控球免疫(这段时间对手抢不回去)。
❌ 失败如果目标超出了抢断范围、或者对手正处于免疫状态:抢断落空,反而是你自己被眩晕 params.clashStunSelf 秒——白白暴露一个大破绽。
务必先检查:抢断前一定要先判断对方的 isImmune(是否免疫)!对一个正在免疫中的持球者抢断,必定失败并让自己眩晕,等于主动送给对手一次机会。养成"先看 isImmune,再决定要不要 clash"的习惯。

3.4 出球 roll(传球和射门是同一个动作!)

mate.roll(x, y, power)把球踢出去的唯一方式。这里有一个非常重要的设定:在本游戏里,"传球"和"射门"其实是同一个动作——都是把球朝某个点滚出去,区别只在于你瞄准的是队友、还是球门。

其中 power(力量)决定球滚多远:

# 最大射程 = power × params.rollRangeFactor
# 如果 power 超过 params.maxPower,会被自动限制到上限
mate.roll(x, y, power)
// 最大射程 = power × params.rollRangeFactor
// 如果 power 超过 params.maxPower,会被自动限制到上限
mate.roll(x, y, power);

3.5 宏指令(Convenience Macros)

为了让你少写代码,游戏内置了一批常用的"宏指令"——它们其实是把常见操作打包好的快捷方法,直接调用即可:

宏指令效果
mate.chaseBall()朝球的方向移动一步(追球)。
mate.shoot()以最大力量把球射向对方球门中心(射门)。
mate.passTo()把球传给你的搭档(你只有一名搭档,所以不用指定传给谁)。
mate.cross()朝远门柱区域横传(cross,即"传中")——这是本竞技场的招牌打法,非常好用。
mate.defendGoal()朝"球—本方球门"的连线上退守一步(回防站位)。

3.6 球员属性(Read-Only Properties)

下面这些属性是只读的(你只能读取、不能修改),用来了解球员当前的状态,方便你的代码做判断:

属性类型说明
x / y数值球员当前所在位置的横 / 纵坐标。
mateId数值0 或 1,用来区分两名球员、分配角色。
color字符串本队颜色,"red" 或 "blue"。
hasBall布尔值当前是否持球(true / false)。
isStunned布尔值是否被眩晕(眩晕时无法移动、抢断或接球)。
isImmune布尔值是否拥有控球免疫(免疫时无法被对手抢断)。
clashReady布尔值抢断技能是否已经冷却好、可以使用。

4. 球与控球机制

Ball & Possession

要踢好球,得先搞懂"球是怎么被抢到、被守住、被拦截的"。这一节的规则决定了控球权的归属,非常值得细读。

4.1 粘球与控球免疫(Sticky & Immunity)

球是"粘"的:一颗无人控制的自由球,只要进入了某名球员的 params.captureRadius 范围,就会自动"吸附"到那名球员脚下,变成他控球。之后,持球者会一直控着球,直到发生下面三种情况之一:他主动出球(roll)、被敌方成功抢断(clash)、或者被守门员扑住。

控球免疫(Possession Immunity):任何一种方式拿到球之后(无论是捡到自由球、抢断得手、还是接到队友传球),新的持球者都会获得 params.possessionImmunity 秒的免疫时间,这段时间里他不会被抢断。换句话说,每一次拿到球,你都保证有一次"从容不被打扰"的行动机会。正因如此,对一个免疫中的持球者抢断必定失败(还会自晕),所以出手前一定要先看 isImmune

4.2 释放气泡与拦截(Release Bubble & Interception)

释放气泡(Release Bubble):一颗刚被踢出、正在滚动的球,在它离开释放点、飞行距离还没达到 params.rollReleaseRadius 之前,任何人都无法接住它(连守门员也不行)。这就好比球刚出脚时外面罩着一层保护泡泡。因此,即使对手就贴在你身边,你也能安全地把球踢出去。而与之相对,一颗完全静止的球,任何时候都可以被捡走。

拦截(Interception):只要你的球员站在滚球的路径上、并且位于释放气泡之外的位置,球飞过来就归你了。由于球的运动轨迹是直线,你完全可以通过 hero.ball 提供的位置和速度信息 (x, y, vx, vy),用数学算出球接下来会经过哪里,从而提前跑位去拦截。

拦截漏洞:被眩晕的球员既不能接球、也不能拦截——所以一名被眩晕的防守者,就等于对方传球路线上凭空出现的一个大漏洞。反过来利用这一点:先抢断对方的防守者制造眩晕,再从他身边穿过去传球或射门,是非常高效的进攻套路。
同一时刻争球是"确定"的:当双方在同一帧同时争抢一颗球时,离球更近的球员获胜;如果距离完全相等,就由两队轮流获得球权。整个判定过程没有任何随机成分——一切都可以计算、可以预测,这也是编写精确策略的基础。

5. 进球与开球

Goals & Kickoffs

红队始终防守左侧球门,蓝队始终防守右侧球门。当球进入某个球门区域(params.gates)时就算进球——而且不管球是谁踢进去的!

当心乌龙球:乌龙球(own goal,把球打进自家门)会算作对手得分,包括自己不小心带球退进了本方球门。所以在任何角色下(哪怕是纯防守的后卫),你的代码都要处理好 mate.hasBall 的情况,别让防守球员糊里糊涂地把球送进自家大门。

每次进球之后(以及每个回合刚开始时),所有球员的位置和技能冷却都会重置回初始阵型,球会被重新放到球场中央,并朝开球方偏移一小段距离 params.kickoffOffset——这样开球方就会确定性地先碰到球

开球权的价值:进球之后,是由刚刚丢球的一方开球(这也符合真实足球规则)。因此开球阶段拿到的控球权,最多只值一次进攻机会,别把所有希望都押在开球上——真正的较量在整个回合的攻防之中。

6. 自动守门员

Auto-Keeper — The Fixed NPC

每个球门都由一名 NPC 守门员把守。它不是 AI Agent,没有人给它写代码——它的行为是系统固定的、完全可预测的。而"可预测"恰恰是你的机会:只要摸清它的规律,就能设计出稳定的破门方法。

特性说明
移动受限守门员只在自己的专属区域(params.keeperZones)内移动,速度为 params.keeperSpeed,并且始终朝"区域内离球最近的那个点"移动。它移动得不快,它的反应滞后,就是你的得分窗口。
物理扑救它的扑救是物理性的、而非"守住整个球门":只有当它的身体真的靠近到 params.keeperCatchRadius 范围内时,才能接住滚来的球。所以快速射向它够不到的远角,就能攻破一个被引偏了位置的守门员。
不可撼动守门员无法被抢断(clash),它手里握着的球也无法被抢走。不过你可以自由进入它的区域,无需顾忌。

解围超级长传(Clearance)Super Kick

守门员拿到球并持球 params.keeperHoldTime 秒之后,会以 params.superKickSpeed 的极快速度,朝离它最近的、且没被眩晕的自家球员射出一道超级长传——直线飞行、中途不减速。关于这道解围球,有几点要知道:

得分要领(记住这一条基本就会破门了):守门员永远追着球移动。所以套路是:先通过带球或向边路传球,把守门员从球门中心引开,等它跑偏之后,再把球射向空出来的远门柱。归根结底,整场比赛拼的就是一场博弈——"球飞到球门需要的时间" vs "守门员挪回位置的反应滞后"。这也是 cross(横传/传中)成为招牌打法的原因:它能把守门员往一侧拽,再打空档。

7. 教练 AI Agent(英雄)

Coach (Hero) — The Main Thread Agent

主线程运行的就是你的教练 AI Agent。和场上跑动的球员不同,教练不亲自碰球,而是像一位真正的主教练那样站在场边:它能看见场上发生的一切,并适时使用各种即时技能(每个技能都有自己独立的冷却时间),从战术层面影响比赛走向。教练是你统筹全局、临场调度的"指挥中枢"。

7.1 教练方法(Methods)

方法说明
hero.wait(duration)让教练的主线程暂停 duration 秒。常用来控制主循环的节奏,避免每一帧都做重复判断。
hero.random()返回一个 [0, 1) 之间的伪随机数。它基于种子生成,每场比赛结果确定(不会真的随机乱变)。
hero.abilityReady(name)检查某个技能是否已冷却好,好了返回 true。技能名有四个:"inspire"、"press"、"whistle"、"distract"。

7.2 四大教练技能(Abilities)

教练 AI Agent 有四个技能,两个用来"增益自己人",两个用来"干扰对手":

技能效果
hero.inspire(mate) 激励让指定的自家球员提速(速度 × inspire.factor),持续 inspire.duration 秒。冷却 inspire.cooldown 秒。适合在反击、追球时使用。
hero.press(enemyMate) 压迫让指定的敌方球员减速(速度 × press.factor),持续 press.duration 秒。冷却 press.cooldown 秒。适合拖慢对方持球者。
hero.whistle(mate) 哨声立刻解除自家球员身上的眩晕。冷却 whistle.cooldown 秒。这是专门克制"抢断流"对手的利器——被抢断眩晕后一声哨响就能立刻恢复。
hero.distract() 干扰敌方守门员减速(速度 × distract.factor),持续 distract.duration 秒。冷却 distract.cooldown 秒。射门前用它,能让守门员更难扑救。
关于返回值:这些技能在成功施放时返回 true;如果技能还没冷却好、或者游戏当前未激活,则返回 false。所以稳妥的做法是:调用前先用 abilityReady(...) 判断一下,避免浪费。

7.3 全场感知(Sensing, 只读)

教练能读取全场信息,这些都是只读属性,是你做决策的"眼睛":

属性说明
hero.ball球的信息 {x, y, vx, vy, owner, heldByKeeper};其中 owner 是当前持球球员的视图,若没人持球则为空(Python 中为 None,C++ 中为 nullptr)。
hero.myMates / hero.theirMates我方 / 对方球员的视图数组,每个含 {x, y, id, color, hasBall, isStunned, isImmune, clashReady}。
hero.myKeeper / hero.theirKeeper我方 / 对方守门员的信息 {x, y, hasBall}。
hero.myGoal / hero.theirGoal我方 / 对方球门的信息 {x, y, width},即球门线上的中心点。
hero.score / hero.opponentScore当前我方 / 对方的进球数。
hero.color你所在队伍的颜色,"red" 或 "blue"。
hero.gameTime整场比赛已经进行的秒数。
hero.roundTime当前回合已经进行的秒数。
hero.round当前回合编号,0(第一回合)或 1(第二回合);本回合剩余时间 = params.halfTime - hero.roundTime
hero.gameActive游戏是否处于进行状态;暂停时为 false(此时所有动作和技能都无效)。

7.4 教练示例代码(Example)

下面这段教练逻辑做了两件聪明事:帮被眩晕的队友吹哨解控,以及在对方持球时压迫拖慢对手。

while True:
    # 谁被眩晕了,就吹哨帮他解除
    for m in hero.myMates:
        if m.isStunned and hero.abilityReady("whistle"):
            hero.whistle(m)

    # 敌方持球时,用压迫拖慢对手
    carrier = hero.ball.owner
    if carrier and carrier.color != hero.color and hero.abilityReady("press"):
        hero.press(carrier)

    hero.wait(0.3)
int main() {
    while (true) {
        // 谁被眩晕了,就吹哨帮他解除
        for (int i = 0; i < hero.myMates.size(); i++) {
            auto m = hero.myMates[i];
            if (m.isStunned && hero.isReady("whistle")) {
                hero.whistle(m);
            }
        }
        // 敌方持球时,用压迫拖慢对手
        auto carrier = hero.ball.owner;
        if (carrier && carrier.color != hero.color && hero.isReady("press")) {
            hero.press(carrier);
        }
        hero.wait(0.3);
    }
    return 0;
}

8. 实战策略

Putting It All Together

把球员 AI Agent、教练 AI Agent、守门员这三套系统串起来,一套稳健、适合新手起步的 AI世界杯思路大致如下:

  1. 先启动线程:代码里务必写上 hero.on("spawn-mate", ...),否则球员 AI Agent 根本不会动——这是最容易忘、也最致命的一步。
  2. 球员分工:用 mateId 区分角色——比如一人主攻、抢球射门,一人拉边接应或回防。每个角色都要处理 hasBall,防止后卫乌龙。
  3. 靠传球推进:记住持球会变慢、追赶者总能追上,所以别硬带球——多用 passTo / cross,甚至撞墙传球来撕开防线。
  4. 抢断先看免疫:调用 clash 前先查对手的 isImmune,避免对免疫者抢断而害自己眩晕。
  5. 攻破守门员:守门员追球移动,先把它引离球门中心,再射向远门柱空档;进攻时可以配合 hero.distract() 拖慢它。
  6. 教练辅助时机:进攻反击时用 inspire 给自家球员提速;防守时用 press 拖慢敌方持球者;自家球员被抢断眩晕时,立刻 whistle 解控。
  7. 追求攻势:多传球、多射门、尽量把球压在对方半场——这些都在积累粉丝支持度,会在打平时救你一命。
终极目标提醒:整套游戏毫无随机性,胜负完全取决于你的计算与策略。进球第一,其次拼支持度——让你写出的 AI Agent 踢得比对手更主动、更聪明

9. 游戏参数速查

Game Parameters

你可以通过 params 对象访问下面所有参数。以下均为默认值——如果文档过时,实际比赛可能采用不同数值,请以游戏内实时的 params 为准。

场地与球门Field & Gates

参数默认值含义
params.field{ minX:4, minY:0, maxX:96, maxY:64 }场地边界
params.gates.red{ x0:0, y0:24, x1:4, y1:40 }红队球门区
params.gates.blue{ x0:96, y0:24, x1:100, y1:40 }蓝队球门区
params.kickoffOffset4开球偏移量
params.halfTime60每回合时长(秒)

球员与移动Mates & Movement

参数默认值含义
params.stepDistance8移动步长
params.mateSpeed20球员基础速度
params.carrierSpeedFactor0.75持球速度系数
params.captureRadius4接球范围

抢断与控球Clash & Possession

参数默认值含义
params.clashRange4抢断距离
params.clashStunTarget1.2抢断成功眩晕对手时间
params.clashStunSelf0.8抢断失败自晕时间
params.clashCooldown3抢断冷却时间
params.possessionImmunity0.5控球免疫时间
params.selfRecaptureDelay0.3自传球接球延迟

出球(滚球)Roll

参数默认值含义
params.rollReleaseRadius8球释放安全半径
params.maxPower100最大踢球力量
params.rollRangeFactor0.45踢球射程系数
params.rollFriction55滚动摩擦系数

守门员Keeper

参数默认值含义
params.keeperSpeed4守门员移动速度
params.keeperCatchRadius3守门员扑救范围
params.keeperHoldTime1守门员持球时间
params.keeperZones.red{ x0:4, y0:20, x1:12, y1:44 }红队守门员区域
params.keeperZones.blue{ x0:88, y0:20, x1:96, y1:44 }蓝队守门员区域
params.superKickSpeed60守门员解围速度

教练技能Coach Abilities

参数默认值含义
params.coach.inspire{ cooldown:6, duration:2, factor:1.5 }激励(提速)
params.coach.press{ cooldown:6, duration:2, factor:0.6 }压迫(减速)
params.coach.whistle{ cooldown:8 }哨声(解眩晕)
params.coach.distract{ cooldown:10, duration:2, factor:0.5 }干扰(拖慢守门员)

粉丝支持度Fan Activity

参数默认值含义
params.activity.pass1成功传球加分
params.activity.clash2赢得抢断加分
params.activity.shot2射门加分
params.activity.ballInOpponentHalfPerSecond0.1控球于对方半场每秒加分

核心速查表 🏆

系统关键命令 / 概念要点
目标进球数 > 对手平局比支持度,仍平则种子掷币
比赛两回合 × halfTime红队先开第 0 回合,蓝队开第 1 回合
支持度传球/抢断/射门/压对方半场显示为教练血量;攻势球胜消极球
球员启动hero.on("spawn-mate", fn)必须写,否则球员不动;用 mateId 分工
移动mate.moveTo / chaseBall一步一决策;持球变慢,会被追上
抢断mate.clash()先查 isImmune,失败会自晕
出球mate.roll(x, y, power)传球=射门;可撞墙反弹;出球不阻塞
shoot/passTo/cross/defendGoalcross 横传远门柱是招牌打法
控球captureRadius / possessionImmunity粘球;接球后有一次免疫窗口
守门员固定 NPC,追球移动引开后射远角;distract 拖慢它
教练inspire/press/whistle/distract即时技能,各自冷却,主线程运行

球员追风,教练运筹,智取门将 ⚽🧠
现在,去 AI世界杯的赛场上,让你的 AI Agent 踢出制胜一球吧!🏆

扣哒世界 编程知识 · 多人竞技场:AI世界杯(Chaotic Crossing Clash)

扣哒世界(CodeCombat)教研团队