TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

Automatic Ruling: From Serves to Winners, and How to Distinguish Warm-ups and Practice from Matches

Status: Draft (2026-10-26), reflects actual measurements. Rules were read from the repository code as of 2026-10-25 (ttscore/, backend/static/tt/play/), and the numbers are values we measured directly from public match footage (dates and conditions vary by table). Accuracy for real ping-pong club footage recorded with a phone is unmeasured as labeled video is not yet available. Writing convention: Measured data is the default; unmeasured items are marked as "Unmeasured".

Table of Contents

1. Automatic Rulings Are "Suggestions," Not Scoreboards

Let us clarify the position first. The true score of a camera-based scoreboard is the history entered and corrected by humans, as seen in Part 2. Automatic rulings merely submit suggestionsto that history one by one—such as "Current rally: Player A scores, reason: out, confidence: 0.83"—and only confirmed suggestions become score entries in the history (backend/static/tt/play/lib/camauto.mjs Introduction, "The Scoreboard Is One"). Therefore, even if an automatic ruling is wrong, there is no need to reconcile it separately from the human-corrected history.

This post follows the journey from the moment ball coordinates come in until chips turn into scores.

  1. Ball coordinates → Events(Bounce, Net, Out, End)
  2. Events → Referee State Machine → Rally Winner
  3. Rally grouping, dead ball, resynchronization
  4. "Needs Review" decisions
  5. Chips and automatic reflection, division of labor between humans and machines
  6. And in between, mechanisms separating warm-ups and practice from matches

How ball coordinates are generated is covered in Part 4, and Part 5of the previous series covers the history of this engine rising from 66.9% to 93.1%. This post provides the full picture of the rules operating currently.

2. From Ball Coordinates to Events

The worker holds ball coordinates for only the last 2.5 seconds (EventFeed, KEEP_S=2.5), filling short gaps (3 samples or fewer) and re-extracting events every time new samples arrive (ttscore/events.py in detect_events). Because it operates in real time and cannot see the future, it continuously re-reads the recent segment.

Bounce: Meeting All Five Conditions

A bounce is emitted only when all of the following conditions are met:

Condition Description Why
V-shape Screen y increases (descending) then decreases (ascending) Basic shape of a bouncing ball
Inside table boundary Inside the table top rectangle. 8px margin per edge, end edges extend up to 0.15m outside the plane At the bounce moment, the ball center is higher than the contact point by the radius (20mm), so mapping to the plane shifts it
Interval 0.12 seconds or more from the previous bounce To prevent counting the same bounce twice
Horizontal direction stability Discard if both forward/backward horizontal movements are 2px or more with opposite directions Balls bouncing on the table keep moving, while balls hitting a racket reverse direction. Racket hits also form a V-shape
Discard V-shape near net Discard V-shape within net x ±2% and net hit ±2 frames Vibration of a ball caught in the net

If recorded from an end view (camera behind the endline), one more condition is checked: longitudinal velocity must be 0.3m/s or greater. This is because end-view screens mix height and distance into screen y, easily producing false V-shapes.

Bounce Side (L/R) in Plan View

Which player's side a bounce lands on is determined by table plane coordinates, not the left and right of the screen. A screen-to-plane (2.74m × 1.525m) homography is created using the four corners of the table (Part 4), and the point where the ball makes contact is mapped to the plane to determine the side using the sign of the longitudinal coordinate u (ttscore/table.py in ball_side). This ensures the formula remains the same whether filmed from the side or the end. If the rectangle is distorted to the point where it cannot be read as a rectangle (likely due to incorrect corners), it falls back to the old rule: left and right based on the net x reference line. This is because plane transformation of a distorted rectangle tilts the net line and flips sides.

Planar projection is accurate only at the exact moment the ball contacts the table . Mapping an airborne ball to the plane yields the intersection point of the line passing through the ball from the camera and the plane; the higher the ball, the further it is displaced. Therefore, planar coordinates are used exclusively for bounce sides, while airborne segments are evaluated separately.

Horizontal Velocity: The Key to Rulings

Longitudinal horizontal velocity is recorded with every bounce: vxright after the bounce and vx_inright before it, with the unit being table lengths/second. Because it is divided by table length rather than pixels, it is independent of camera distance and resolution. This single number distinguishes serves, returns, and false bounces in the referee section below.

Nets, Outs, and Ends

Confidence

Every event is assigned a confidence score. conf = min(검출 신뢰도, 공백 신뢰도) , and gap confidence is 1 − 놓친 프레임 / (fps × 0.25초) . If the ball was lost for more than 0.25 seconds prior, it is 0, meaning it cannot be trusted because a bounce might have been missed in between. Detection confidence is also tracked separately (det_conf), used in the first bounce of a serve.

3. Referee: Ignoring "Who Hit It"

서브 대기 서브 시작만 받는다 직행 서버 쪽 바운드를 못 봄 서브 진행 네트 닿음 = 렛 후보 랠리 기대하는 쪽만 들고 간다 (받아야 할 쪽 S) 넘겨주기·튀기기 손에 든 공은 버리고 기다림 서버 쪽 바운드 네트 쪽 ≥ 0.7 리시버 쪽, 서버 쪽에서 온 방향 ≥ 0.7 리시버 쪽 바운드 1초 안 서버 쪽에 리턴 기대한 쪽 바운드 → 반대쪽을 기대 또·끝: 서버 득점 S 쪽 선수가 못 받음 서브 실패 판정 → 점수판의 득점 같은 쪽에 또 바운드 → 투 바운드 · 밖으로 → 아웃 아무 일 없이 끝 → 못 받음 · 서브 실패 → 리시버 득점 렛 → 점수 없음 0.25초 안 같은 쪽 두 바운드와 렛은 다음 이벤트까지 미룸 「누가 쳤나」는 보지 않는다. 바운드 순서와 가로 속도(테이블 길이/초)만 본다.
Figure: Referee State Machine: From Wait for Serve to Serve in Progress / Direct, Rally, and End

The core concept of the referee (ttscore/referee.py in RallyReferee) is stated in the very first line of the introduction: Who hit the ball is inferred from the bounce order rather than directly observed from video. If a ball bounces on side S, the player on side S must hit it next so that it lands once on the opposite side. Failing to do so (bouncing on the same side again, going out without bouncing, or ending without incident) results in the player on side S losing the point. The serve is the only exception, requiring one bounce on the server's side followed by one on the receiver's side.

Racket contact is the hardest event to find in video. Balls are obscured by rackets, motion-blurred, and blended into players' bodies. This design scores points without relying on that event. The trade-off is clear: if a single bounce is missed or a false bounce slips in, "whose turn it is to hit" breaks down entirely. Therefore, most of the rules below serve as mechanisms to filter out false bounces and balls outside of rallies.

There are four states: serve0(Wait for Serve) → serve1(Serve in Progress) or direct(Direct) → rally(Rally) → done(End).

Wait for Serve: Accepting Only the Start of a Serve

Even between rallies, the ball moves a lot. Players go fetch balls, pass them to opponents, bounce them on their own side a few times before serving, and hand-held balls shake. If these are captured as events, nonexistent points are created. In fact, in the first run where an entire match was fed into the engine, 20 out of 49 rulings were not rallies (13 ball passes, 5 pre-serve self-bounces, etc., 2026-10-11, docs/tt/PERFORMANCE.md 2.6).

Therefore, in the Wait for Serve state, only balls that look like the start of a serve are accepted.

# ttscore/referee.py _start() 에서 줄임
f = self._forward(ev, self.server, serve=True)    # 서버 쪽에서 네트 쪽으로 가는 가로 속도(바운드 앞뒤 중 큰 쪽)
if ev.side == self.server:
    if f is not None and f < SERVE_MIN_VX:        # 0.7 테이블 길이/초
        return False                               # 서브가 아니다 — 버리고 계속 기다린다
    self.phase = "serve1"
    return True
if f is not None and f >= TOWARD_NET_VX:          # 리시버 쪽에, 서버 쪽에서 날아온 방향으로 떨어진 공
    self.phase, self.expect = "direct", self.server
    self.conf = min(self.conf, AMBIGUOUS_CONF)     # 0.5 — 서버 쪽 바운드를 못 봤으니 확인 필요
    return True
return False

There are two conditions: it must land on the server's side known to the scoreboard, and move toward the net at 0.7 table lengths/second or more . Other balls are discarded without penalty to confidence. The justification for the 0.7 figure is empirical (12 public matches, 2026-10-11).

Balls starting "like a serve" on the side opposite the scoreboard's server are not accepted. When the receiver tosses the ball back to the server, it bounces from the receiver's side toward the net and lands on the server's side. It looks identical to a serve but moves in the reverse direction. Accepting serves from both sides increased false rulings in 5 matches from 3 to 16.

There is one exception: a ball landing on the receiver's side in the direction flying from the server's side(moving away from the net at 0.7 or faster). This could be a serve where the server-side bounce was missed, or a failed serve that skipped the server's side. This is accepted as a direct(Direct) state, lowering confidence to 0.5 and sending it for review. First bounces of serves are frequently missed due to being blocked by tosses and rackets. Therefore, if another bounce occurs on the receiver's side or it ends directly after a direct event, depending on the switch (TTSCORE_DIRECT_MISS, enabled), it is judged as an unreturned ball by the receiver (server point) . In actual video experiments (Director E27), 48 out of 60 "another bounce" rulings and 36 out of 49 "end" rulings were server points.

Serve in Progress: To the Receiver's Side Within 1 Second

serve1 In

Let: Postponing Until the Next Event

If the ball touches the net during a serve (net) If it bounced on the receiver's side, it is a let candidate. Instead of declaring a let immediately, it is deferred until the next event.

Initially, lets were called immediately, but in the real-time pipeline, it happened twice that the "dead ball" rules following a let call (Section 4) discarded all subsequent bounds of the actual ongoing rally, causing the rally score to disappear (2026-10-13). The deferred version fixed this issue, and the offline rally-level results remained unchanged.

Rally: Flipping the expected side

In a rally, only the "side where the next bound should land" (expect) is tracked.

# referee.py feed() 의 랠리 부분에서 줄임
f = self._forward(ev, ev.side)                 # 바운드 뒤 가로 속도
if f is not None and f >= TOWARD_NET_VX:       # 네트 쪽으로 가는 「바운드」 는 가짜 — 버린다
    return None
if ev.side == self.expect:                     # 기대한 쪽: 정상. 다음은 반대쪽
    self.expect = flip(self.expect)
    return None
if ev.t - prev_bounce_t < DOUBLE_BOUNCE_SURE_S:   # 0.25초 안의 같은 쪽 두 바운드 — 미룬다
    self.pending = ev
    return None
return self._call(flip(ev.side), "double_bounce", ev)   # 같은 쪽에 또: 그 쪽 선수가 진다

Out and End: The player on the last bouncing side failed to receive it

Judgment Confidence

The confidence of a single judgment is the minimumvalue of the event confidences within that rally. However, the first bounce of a serve is counted using the detection confidence excluding gaps (det_conf). This is because the gap before the serve's first bounce is caused by occlusion from the toss and racket, and there are no bounds to be missed in between. Before excluding this, 70% of the serve's first bounces had a confidence of 0, which was the main cause of "86% needs review" (PERFORMANCE 2.6).

4. Grouping Rallies: Dead ball, Winner, Resynchronization

A single referee handles a single rally. The task of chaining rallies together to form a scoreboard is performed by ttscore/analyze.py of LiveScorer .

5. "Needs Review": Four Branches

Every judgment can be marked with a "needs review" flag. This leaves the judgment as-is while asking a human to take a look. There are four ways this flag is attached.

Branch Condition Reason (why)
① Calibration Regression "Probability that the judgment is incorrect" calculated by the learned logistic regression > 0.0442 (Calibration v2). When calibration is turned off, the old rule applies: "confidence < 0.6" cal + top two feature bundles that pushed it the most (weak ball, missed ball, long rally, unclear end, unobserved serve, mismatched serve side, judgment type)
② End-line Band Landing position is within ±0.08m of the end line boundary edge
③ Signal Discrepancy / Weak Ball If ball models disagree, or if the ratio of frames where the ball is visible in the rally section is less than 0.5, confidence is reduced to 0.59 conflict etc.
④ Table Rally is re-locating the table or spans a missing section table

Judgments accompanied by a resynchronization signal (resync) are also prompted separately in the chip.

Why a single confidence score is not enough

The initial rule was "needs review if rally confidence < 0.6." When tested on 2026-10-14 to see if this confidence actually distinguishes errors, it failed to distinguish them(Ledger E20, live pipeline as-is, public test 7 games × 11 degradation conditions, 597 judgments). The accuracy in the 0.0–0.1 confidence range was 0.86, and in the 0.9–1.0 range it was also 0.86, with an AUC of 0.51 (coin flip) for distinguishing errors. This is because, in the 30fps live pipeline, even a single missed view of the ball drops the minimum value below 0.6.

Therefore, a small logistic regression was trained using 19 values already known to the engine at the moment of judgment (ttscore/reviewcal.py). These include confidence, minimum detection confidence, average bound confidence, number of bounds and events, number of weak and gap events, rally length, whether the end is an "end", whether a serve pattern pair was observed, whether that side differs from the scoreboard, and the reason for the judgment. The calculation is a single dot product per judgment, incurring zero real-time cost. The principles and training are covered in Part 4.

Judgment Condition Incorrect without review (†) Needs Review Error Discrimination AUC
Confidence < 0.6 Test 7 games × 11 conditions (597 judgments, E20) 26 395 0.51
Calibration v1 Same 12 430 —
Calibration v2 (Production) Test 7 games × 11 conditions (661 judgments, reflecting label corrections, E25) 20 → 8 450 → 497 0.71 → 0.73

The part before 「→」 in the v2 row is v1 under the same condition. †The v2 row includes ghost judgments that passed without review, so it cannot be directly compared with the top two rows. Calibration selected thresholds to slightly increase needs-review while drastically reducing false answers that humans blindly accept . Thresholds were determined via 5-fold cross-validation on training games as "minimum missed errors among thresholds where needs-review is at or below the current level."

The reason for attaching reasons also emerged from actual measurements. Before calibration, reasons were empty, so the chip could not tell what to review (Ledger E19-2). Now, for every judgment determined to need review by the regression, up to two human-readable feature bundles "that pushed most toward being incorrect" are sent along.

End-line Band

Whether a ball hit the end line (edge) or fell just outside is the hardest judgment to make with a single camera. If the landing spot is within ±0.08m of the end-line boundary, the winner is kept as-is and sent for review (TTSCORE_EDGE_REVIEW, Ledger E34).

6. How Warm-ups, Practice, and Matches are Distinguished

① 휴대폰 화면 몸풀기: 득점 칸을 눌러도 점수 안 들어감 「경기 시작」 → 시작 시각을 이력에 적음 ② 자동 점수 모듈 기기 시계 → 프레임 시계(연결 순간 = 0) {type: score, on, start, server_side} ③ 웹 중계 로그인 · 몫 확인 뒤 그대로 전달 ④ 실시간 작업자 scorer.start(server_side, t) 지난 판정·구간을 잊고 새로 시작 ⑤ 엔진 시각 t 앞의 이벤트는 모두 버림 그 뒤에만 심판이 판정 이중 잠금: 시작 전에는 브라우저가 신호를 안 보내고 작업자도 판정하지 않는다(공 위치만 보냄). 서버 쪽은 점수판이 바뀔 때마다 따로 보낸다 — 사람이 고친 쪽이 이긴다.
Figure: The path of the "Match Start" signal from the mobile screen to the engine

The Engine Cannot Distinguish Practice Serves

Table tennis matches begin with a warm-up rally. Players exchange hits for a few minutes and try out practice serves. The problem is that practice serves look identical to real serves. They bounce once on the server's side, once on the receiver's side, and the net-crossing speed is the same as the real ones.

There is a measured case. In one public test game (test_4), a pre-match practice serve was captured at 2.05 seconds from the start. It went from the server side toward the net at 4.2 table lengths/second, and the ball crossed the net. Under the serve recognition rules in Section 3, there is no way to distinguish it from a real serve. The engine counted this as the first point, and that single point misaligned the serve turn until the end of the match, resulting in 0 out of 21 rallies with correct post-rally scores for this game (PERFORMANCE 2.6).

Methods to distinguish practice using only video were also tested: "Discard before the first complete serve of the scoreboard's first server side" (N1) and "Discard before the first serve pattern pair on either side" (N2). Neither improved results (compared to engine without signal: 5-game score correctness 24 → 24, winners 41 → 40, Part 12 total no-judgment 18 → 21). In test_4, the real first serve was a missed serve hitting the net and thus was not a "complete serve," and both rules discarded even the first real point. Since there was only one practice serve case, rules could not be determined from data alone. Therefore, it was not included.

The conclusion is simple: Humans inform when the match starts. This is easy for players, but nearly impossible for machines.

Path of the "Match Start" Signal

The camera scoreboard's warm-up state (Part 2) is the gateway to receive this signal.

  1. Screen. When the camera is turned on, it is in the warm-up state (CamMatch.startedAt === null). Tapping the score box does not add points, but shakes the "Match Start" button. The remote control's start key only works during warm-up.
  2. Start. When "Match Start" is pressed, startMatch() records the match start time in the history and triggers a tt:match-start event to generate {type:'score', on:true, start, server_side} .
  3. Time Synchronization. The automated score screen (cam-auto.js) receives it, calls scoreOn() , and converts the start time recorded by the device clock (Date.now) into a frame clock(where the moment of connection with the worker is 0) (startOffset). This is because workers do not know the device clock and only know timestamps attached to frames.
  4. Worker. Via web relay, the worker's session calls scorer.start(server_side, t) and creates a new LiveScorer . Previous judgments and sections are forgotten, and all events prior to this time are discarded.
  5. Double Lock. The browser does not send this signal if the match has not started, and the worker does not make judgments before receiving the signal (only ball positions and bound indicators are sent). The browser side also does not generate proposals for judgments arriving during warm-up or after the match ends (autoProposal is null, code comment "Warm-up rallies are not scores").

The server side also follows along. Every time the scoreboard changes (scoring, undo, human correcting the service turn), the current server side is sent to the worker, and the engine uses it to align the server side for the next rally. The server side corrected by a human wins.

Measurement: Effect of a Single Signal

This is a value measured by simulating that a person pressed the "Start Match" signal just before the first serve across 5 public matches (49 rallies) from 0:0-starting labels matching the rules (2026-10-11, PERFORMANCE 2.7).

Metric (5 matches, 49 rallies) No Signal With Signal
Correct Post-Rally Score (Score/Game) 27 / 49 36 / 49
Correct Winner 44 / 49 45 / 49
Non-Rally Judgment (Fake Score) 3 2

One test_4 improved from 0/21 to 9/21. The results were identical even when the signal was pressed 0.3, 1, 2, or 3 seconds before the first serve, but 5 seconds before(before the practice serve) had no effect. Therefore, the screen instructs users to press it "right after warm-up and just before the first serve."

"Practice-like Balls" During a Match

Even after a match starts, balls continue to move between rallies. The mechanisms in Sections 3 and 4 filter these out.

Remaining Limitations

"Practice" Features Unrelated to the Scoreboard

Two similarly named, easily confused features are not connected to the scoreboard or automatic judgment.

7. Chips: Division of Labor Between Humans and Machines

From Judgment to Chip

작업자 판정 승자 쪽(L·R) · 이유 · 신뢰도 · 확인 필요와 사유 칩 — 그때의 왼쪽·오른쪽 선수로 바꿔 띄움 줄은 2개까지 · 최소 2.5초 보임 · 렛과 판정 없음은 안 띄움 칩에 답 맞음 선수 이름 무효 득점 칸을 누름 그 손이 칩의 답 (2점 막기) 답 없음 확실: 4초 뒤 확인 필요: 9초 뒤 그대로 넣음 이력(진짜 점수)에 득점 칸 맞음·같은 선수 → confirm · 다른 선수 → flip 무효 → 넣지 않음, void 시간이 지나 들어간 판정 → 고친 기록 남기지 않음 고친 기록은 영상 없이, 동의 표시와 함께 서버로
Figure: Path and timing rules for judgments to emerge as chips and populate the score column

When an operator submits a judgment, the browser converts it relative to the left player at that moment and displays it as a chip. This is because operators refer to L/R from the raw video, screens can be mirrored, and positions swap every game. Lets and no-judgments are not suggested.

Chips feature four buttons: "Confirm", "(Left Player) Scores", "(Right Player) Scores", and "Void".

Auto-Commit When There Is No Response

No one will tap a distant phone after every single rally. Therefore, unresponded items are auto-committed (backend/static/tt/play/lib/scorestatus.mjs).

Judgment Timeout Without Response Rationale
Certain 4 seconds —
Needs Review 9 seconds Ample time for a human to look at the chip and respond (approx. 2.5 seconds + 3 seconds for "Hold on"), fitting within the actual inter-judgment intervals in video (3.8–17.4 seconds).

This rule has a backstory. When public match footage was streamed to operations staff on 2026-10-13, the camera scoreboard received four judgments over 50 seconds yet remained at 0:0. Most judgments were marked Needs Review, and the screen withheld them until a human responded, discarding them if a new judgment arrived. Furthermore, it provided no explanation for why they were dropped. Now, Needs Review items are also committed after 9 seconds, and if a new judgment arrives before the 9-second mark, the preceding judgment is committed immediately.

Chip queues are capped at two. When a third judgment arrives, the oldest is auto-committed, and any displayed chip remains visible for at least 2.5 seconds.

Handling Responses

Response Recorded History Column Revision Log
"Confirm" Score (Source: auto) confirm
Auto-commit after timeout Score (Source: auto) None (not human-verified)
Same player as judgment Score (Source: auto) confirm
Different player from judgment Score (Source: tap) flip
"Void" Not added void

Manual Input Overrides Chip Responses

This is the most critical rule: if you tap the score column while a chip is displayed, that manual action becomes the response to the chip(handOverride). If it is the same player, it counts as confirm; if a different player, it counts as a correction. Without this rule, operators would habitually tap manual scores while automatic judgments commit 4 seconds later, resulting in 2 points per single rally.

In summary, roles are divided as follows.

Human Machine
When the match starts Determines("Start Match") Discards everything before it
Who serves Can correct; correcting updates the winner Adopts from scoreboard, asks "Is the serve correct?" if mismatched
Rally winner Responds to chips or uses manual tap (manual action is the response) Suggests. Commits after 4/9 seconds if no response
Ambiguous judgments Reviews Appends Needs Review status and reasons
Miscalculated scores Erases from history (Part 2) —

Records of human responses to chips (revision logs) may be retained on the server. Video footage is omitted, and only data consented to for training purposes is exported. Where these records go and how they have been used is covered in Part 6.

8. Empirical Measurements: Current Accuracy Status

All rows are measured from public match videos (7 OpenTTGames test matches). Different matches were used for training and threshold tuning, with test matches evaluated last. However, since the rule engine itself was refined while reviewing test matches between 2026-10-11 and 12, test scores for the rule baseline are optimistic (Director's data leakage warning).

Measurement Conditions Result
Operational Workflow Baseline Table 7 test matches × 11 degradation conditions, 638 labels, production-identical pipeline + bounce filtering, calibration v2, direct-pass rules (2026-10-16, Director E25/E27) Confirm 508, Incorrect without review 8, Needs Review 497, Phantom (scored with no ball present) 51, Judgment latency p90 1.32s
Camera Scoreboard End-to-End Streaming 851 seconds of 7 test matches as mock camera 30fps into actual browser/operator code, 58 labeled rallies (2026-10-17, Director E35) Confirm 47, Incorrect 7, No judgment 4, Phantom 9, Incorrect without review 0, Needs Review 53/65
Public Footage by Condition 8 shooting conditions across 27 clips (2026-10-14–16, PERFORMANCE 2.16–2.17) Vertical smartphone footage yielded 5% ball-visible frames and 0 judgments
Actual Table Tennis Hall Shot on Smartphone — Unmeasured(0 labeled videos)

These two lines summarize the current characteristics: incorrect judgments without review are rare, but Needs Review frequency is high. In the end-to-end camera scoreboard test, 53 out of 65 judgments were marked Needs Review. The 9-second auto-commit bears this burden on behalf of humans, and our next improvement goal is to "reduce Needs Review items without increasing unreviewed errors."

9. Conclusion

A word from TT. Warm-up serves and real serves look completely identical to me. Therefore, please be sure to press "Start Match" immediately before the first serve. That single action is more important than ten points.

References

  1. International Table Tennis Federation. ITTF Statutes 2026, Chapter 2: The Laws of Table Tennis, §§2.10–2.15. https://db.ittf.com/document
  2. R. Voeikov, N. Falaleev, R. Baikulov. "TTNet: Real-time Temporal and Spatial Video Analysis of Table Tennis." CVPR Workshops, 2020. arXiv:2004.09927. A representative prior study where neural networks directly detect bounces and nets; our approach differs in extracting events via ball trajectory rules.
  3. Extended OpenTTGames (Rally End Reason & Winner Labels). arXiv:2512.19327. Rally winner ground truth for this article.
  4. Our Measurements: Repository docs/tt/PERFORMANCE.md 2.6, 2.7, 2.16, 2.17, Research Logs E20, E25, E27, E34, E35. Code ttscore/events.py·referee.py·analyze.py·reviewcal.py, backend/static/tt/play/lib/camauto.mjs·scorestatus.mjs.

Further Reading

Sign in to like

Comments

No comments yet.

Sign in to leave a comment