timotheeg / nestrischamps

A web-based OCR and restreamer system for NES Classic Tetris players
MIT License
45 stars 11 forks source link

Line count incorrect due to single misread #176

Closed Javantea closed 1 month ago

Javantea commented 2 months ago

Game 3 of Dog vs Fractal in CTL had a bad render due to a single misread of the line count. As the line count incremented from 99 to 100, the game read 488 and then incremented to 490. This can be avoided by refusing to increment the line count by larger than a certain amount. Another possible solution is to note a wide discrepancy between line count OCR and current line count 100 vs 490 and allow the software to adjust accordingly.

https://nestrischamps.io/replay/classic/419305?bg=2&speed=2&ts=255406

https://www.twitch.tv/videos/2156178342?t=2h9m21s

timotheeg commented 2 months ago

I pushed an increment of 1 frame to the frame buffer to address that: https://github.com/timotheeg/nestrischamps/commit/6cb2736a6ba2bf1f89c243a1c39fb45ca7fab481

Longer explanation for reference below: the (likely?) interlacing artefacts caused the transition 099->100 to be read temporarily as 488, and that triggered a cascade of issues.

488 is above 340, so NTC took over lines progression by computation from the unit digit, rather than by OCR. so when the line count stabilised to 100, unit digit was 0, and so progression went from 488 to 490. which is level 55. Since the lines progression was then taken over from the unit digits, it could never recover and kept increasing from there for the rest of the game.

Score/lines/level are "stable" metrics, meaning that they change rarely, and stay consistent in between changes. NTC already had a mechanism (2-frames buffer) to delay the read and get the actual stable value. I don't know why the 2 frames were not enough for Dog's case, but here's hoping 3 will be.

I'm not keen in putting intelligence into the OCR to restrict what can and cannot be accepted. In the worst of case, it will jut block every read and do nothing at all. The initial idea of NTC was that the producer side would be pure OCR, while the renderer can decide what to do with the data. This also make things easier to troubleshoot because we can look at the data stream to know what was captured.

The later level of play (crash games rebirth games) have forced me to introduce compromises to that, since the level is not displayed consistently between gym and classic rom, NTC now derives it from the line count from level 30 onward, so that's not OCR. The line count itself goes in the thousands now, so rather than OCRing everything and introducing rollover, NTC took over lines progression from the unit digits, so it's also not OCR.

In theory, if calibration is good, these would work well, but if something goes wrong, it can't self recover any more 😢

Javantea commented 2 months ago

Thanks for the detailed explanation and quick fix. This should help us understand better what is going on. I'll see if I can find a way to reproduce this issue. I see why you would like to keep the producer limited to OCR. That seems fine, we can definitely fix the bug in the renderer if it isn't already fixed.

timotheeg commented 2 months ago

Hey @Javantea, question btw: how did you find Dog's game id to show the issue in a classic replay link? Did Dog gave it to you?

Javantea commented 2 months ago

I read the source code of the server and found the /api/games/id url, then I tested https://nestrischamps.io/api/games/21167 which I thought was my own game which turned out to not be mine. Then I did a search for the game looking at the date of the json, first increasing to 421167 by 100000s, then 321167 to 421167 by 10000s. Then to 419267 by 1000s. Once I was within 100 of the game, I wrote this script: for i in $(seq 419268 419366); do curl -o ntcdl/"$i".json https://nestrischamps.io/api/games/"$i"; sleep 1; done At which point I was able to find the right one by grepping for a piece sequence I found: grep -l ISLZ $(grep -l 'login":"dogplaying' ntcdl/*.json ) The total number of requests was around 130 to get the game, ~100 of those being in the loop.

timotheeg commented 1 month ago

Thanks for giving the info. That's great detective work you did 😄👏

Right, so the games files are indeed all public in NTC (which is how replays can work for any game). I initially didn't put any thought in making the ids easy to find though to have some semblance of privacy. When I search for a game, because I have access to the DB, if I have the player login name, and the time and the final score, I can easily find the game.

That said, it takes time for me to do that every time, so with more error reports coming in, and with finding the game ids always the first thing to do, I'll try to make it easier to find game IDs, so you all can help me speed up troubleshooting by providing the game id directly :)

By the way, since you are looking around in the codebase, here are some further info I do when I troubleshoot:

  1. I always play the game in the classic renderer, being able to navigate clear by clear, piece by piece, or frame by frame, typically makes it quick to reveal what goes wrong
  2. if I need access to the full frame data, there's a script get_verification_data, which you can run to get a few local files from the game data that are kind of human readable :)

E.g.:

cd scripts
node get_verification_data.js 419305

Thanks for all the issue reports. I appreciate it :)