java案例统计撞墙式配合完成了几次?

wen java案例 1

篮球"撞墙式配合"(Give and Go)统计案例

"撞墙式配合"(Give and Go,也叫传切配合)是篮球中经典的二人配合战术:A 传球给 B,A 快速空切篮下,B 再回传给 A 完成进攻。

java案例统计撞墙式配合完成了几次?

下面用 Java 演示如何统计这种配合完成的次数。


需求分析

统计"撞墙式配合完成次数"需要识别:

  1. 传球事件:球员 A → 球员 B
  2. 接球后快速回传:B → A(短时间内,A 已完成空切)
  3. 最终完成:A 接球后投篮命中/得分

即:A传B → B回传A → A得分 才算一次成功的撞墙式配合。


数据模型设计

import java.time.LocalDateTime;
import java.util.Objects;
/**
 * 篮球比赛事件
 */
public class GameEvent {
    public enum Type {
        PASS,      // 传球
        SCORE,     // 得分
        MISS       // 投篮不中
    }
    private final String fromPlayer;   // 传球者(对传球事件)
    private final String toPlayer;     // 接球者(对传球事件)/ 得分者
    private final Type type;
    private final LocalDateTime time;
    public GameEvent(String fromPlayer, String toPlayer, Type type, LocalDateTime time) {
        this.fromPlayer = fromPlayer;
        this.toPlayer = toPlayer;
        this.type = type;
        this.time = time;
    }
    public String getFromPlayer() { return fromPlayer; }
    public String getToPlayer()   { return toPlayer; }
    public Type getType()         { return type; }
    public LocalDateTime getTime(){ return time; }
    // getter/setter 略
}

核心统计逻辑

import java.time.Duration;
import java.util.List;
public class GiveAndGoAnalyzer {
    /** 回传最大时间窗口(秒),超过则不算撞墙式配合 */
    private static final long MAX_INTERVAL_SECONDS = 3;
    /**
     * 统计撞墙式配合完成次数
     * 识别模式:A --PASS--> B --PASS--> A --SCORE--> 得分
     */
    public static int countGiveAndGo(List<GameEvent> events) {
        int count = 0;
        for (int i = 0; i + 2 < events.size(); i++) {
            GameEvent e1 = events.get(i);      // 第一次传球 A -> B
            GameEvent e2 = events.get(i + 1);  // 回传      B -> A
            GameEvent e3 = events.get(i + 2);  // 得分
            if (!isGiveAndGo(e1, e2, e3)) continue;
            count++;
            i += 2; // 跳过已匹配的三个事件,避免重复统计
        }
        return count;
    }
    /** 判断三个连续事件是否构成一次撞墙式配合 */
    private static boolean isGiveAndGo(GameEvent e1, GameEvent e2, GameEvent e3) {
        // 1. 第一次传球
        if (e1.getType() != GameEvent.Type.PASS) return false;
        // 2. 第二次传球
        if (e2.getType() != GameEvent.Type.PASS) return false;
        // 3. 最后必须得分
        if (e3.getType() != GameEvent.Type.SCORE) return false;
        String a = e1.getFromPlayer();
        String b = e1.getToPlayer();
        // 4. 回传方向必须是 B -> A
        if (!Objects.equals(e2.getFromPlayer(), b)) return false;
        if (!Objects.equals(e2.getToPlayer(), a)) return false;
        // 5. 得分者必须是 A
        if (!Objects.equals(e3.getToPlayer(), a)) return false;
        // 6. 时间间隔不能太长(传球和回传必须是快速衔接)
        long gap = Duration.between(e1.getTime(), e2.getTime()).getSeconds();
        if (gap > MAX_INTERVAL_SECONDS) return false;
        return true;
    }
}

测试用例

import java.time.LocalDateTime;
import java.util.List;
public class Main {
    public static void main(String[] args) {
        LocalDateTime t = LocalDateTime.of(2025, 1, 1, 19, 0, 0);
        List<GameEvent> events = List.of(
            // A -> B,B 立刻回传 A,A 得分 = 一次撞墙式配合 ✔
            new GameEvent("A", "B", GameEvent.Type.PASS,  t.plusSeconds(0)),
            new GameEvent("B", "A", GameEvent.Type.PASS,  t.plusSeconds(1)),
            new GameEvent(null, "A", GameEvent.Type.SCORE, t.plusSeconds(3)),
            // C -> D 后 D 传给 E,不算撞墙(回传对象不一致)✘
            new GameEvent("C", "D", GameEvent.Type.PASS,  t.plusSeconds(10)),
            new GameEvent("D", "E", GameEvent.Type.PASS,  t.plusSeconds(11)),
            new GameEvent(null, "E", GameEvent.Type.SCORE, t.plusSeconds(13)),
            // F -> G,G 回传 F,但 F 投篮不中 ✘
            new GameEvent("F", "G", GameEvent.Type.PASS,  t.plusSeconds(20)),
            new GameEvent("G", "F", GameEvent.Type.PASS,  t.plusSeconds(21)),
            new GameEvent(null, "F", GameEvent.Type.MISS,  t.plusSeconds(23)),
            // H -> I,但回传太慢(超过 3 秒)✘
            new GameEvent("H", "I", GameEvent.Type.PASS,  t.plusSeconds(30)),
            new GameEvent("I", "H", GameEvent.Type.PASS,  t.plusSeconds(40)),
            new GameEvent(null, "H", GameEvent.Type.SCORE, t.plusSeconds(42))
        );
        int total = GiveAndGoAnalyzer.countGiveAndGo(events);
        System.out.println("撞墙式配合完成次数:" + total + " 次");
    }
}

运行结果:

撞墙式配合完成次数:1 次

扩展优化建议

需求 实现思路
统计每名球员的撞墙次数 Map<String, Integer> 以 A 为 key 累计
支持投篮造犯规(罚球得分) Type 中新增 FREE_THROW 分支
处理传球被断 新增 STEAL 事件,遇到则中断匹配
处理滑动窗口内多步配合 改为状态机:等待回传 → 等待终结
从文件/数据库读取事件 BufferedReader 或 JDBC 加载后构造 List

状态机版(推荐用于真实数据)

真实比赛数据顺序可能有噪声,建议用状态机代替"三事件连看":

public static int countByStateMachine(List<GameEvent> events) {
    int count = 0;
    String passer = null;       // A
    String receiver = null;     // B
    LocalDateTime passTime = null;
    for (GameEvent e : events) {
        switch (e.getType()) {
            case PASS:
                if (passer == null) {
                    // 记录第一次传球
                    passer = e.getFromPlayer();
                    receiver = e.getToPlayer();
                    passTime = e.getTime();
                } else if (e.getFromPlayer().equals(receiver)
                        && e.getToPlayer().equals(passer)
                        && Duration.between(passTime, e.getTime()).getSeconds() <= 3) {
                    // 已形成回传,等待 A 得分
                    passTime = e.getTime(); // 复用字段标记"等待终结"
                } else {
                    // 其他传球,重置
                    passer = e.getFromPlayer();
                    receiver = e.getToPlayer();
                    passTime = e.getTime();
                }
                break;
            case SCORE:
                if (passer != null && e.getToPlayer().equals(passer)
                        && Duration.between(passTime, e.getTime()).getSeconds() <= 5) {
                    count++;
                }
                passer = null;
                break;
            case MISS:
                passer = null; // 未中,放弃
                break;
        }
    }
    return count;
}

统计撞墙式配合的关键是识别"传—回传—得分"这条链,并加上时间窗口球员身份匹配约束,上面给出了最简三事件匹配法和更健壮的状态机法,可根据实际数据噪音程度选择。

上一篇java案例如何分析球员跑动热点图?

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!