Closed timotheeg closed 4 years ago
The B
or T
pattern could be used for the piece stats as well š¤.
One of the longest game of tetris I've seen (Jake's Marathon to PAL maxout) played 1008 pieces in total, and each type had less than 200 count in the end.
Of course random being random, it is possible to have piece distribution as (1000, 0, 0, 0, 0, 0, 0)
, but that's super unlikely, and would probably be death before reaching 200 or 300 piece of that one kind.
Right; I can see why TD is superior to AA for the general case. If you also instead wrap it as a config setting (Boolean allow levels past 29) that would be swell and then we can make TD the default.
Wrt to pieces, BDD seems to be a sensible default, however we technically only need the last digit.
The next optimization we actually want is to update text only once per piece, by scanning top of field. I think I discussed this previously. This would give a more than 1000x speed increase imo. We can also optionally queue text read over several frames; this helps for peasant PCs.
I.e. every frame scan field (or top of it)
When new piece is detected, scan score.
If it changes, scan level and lines.
Scan last digit of piece distribution only, checking for += 1.
If you can guarantee no frame drops, I already have a text-free piece distribution method. This is 1000s of times faster than doing ocr.
Right; I can see why TD is superior to AA for the general case. If you can instead wrap it as a config setting (Boolean allow levels past 29) that would be swell and then we can make TD the default.
I'll do that for now. Commit coming soon(-ish).
Right; I can see why TD is superior to AA for the general case. If you can instead wrap it as a config setting (Boolean allow levels past 29) that would be swell and then we can make TD the default.
I'll do that for now. Commit coming soon(-ish).
Also we need not scan level either; we can scan level at begininng of game, then increment level every 10 lines.
We need not scan lines either; we can scan it once at beginning to check for 0, and then scan just the last digit to check for lines cleared.
Atm we check for new game as
if last.lines== invalid and last.score == invalid and notlevel.isvalid:
if (lines == 00 && score == 00 && level.isValid): newgame
That might have to be tweaked.
Lots of places to improve performance vastly!
let me know when you're done and i will press merge :D
Done! (I just forced push a minor change to one of the comment)
Should be clean now :)
Your suggestions for improvements are all great! š
I'll see if I can do some of it. To be honest though, as long as things are working well enough on my box, I lose interest a bit š , and try to focus on the next part of the pipeline instead (rendering, vs. mode, etc.)
BTW, As you mentioned, I always have NESTris OCR do full field scanning, and I use that to figure out when new piece is released without worrying of missing a frame at the top of the field. Basically, as long as I see a +4 between current count and past count, we know a new piece has appeared.
I liked that +4 approach, because it works even if I drop some frames when the piece is at the top. But to be really reliable, ideally no frame drop is best no matter what of course.
My code looks like this:
// assume able to set up initial block count and self correct
// assume no frame drop
let
pending_piece,
pending_single,
last_block_count,
clear_animation_remaining_frames;
function process_game_frame() {
if (pending_piece) {
onPiece(); // reads what needs to be read (preview, das, etc.)
pending_piece = false;
}
if (clear_animation_remaining_frames-- > 0) return;
const block_diff = cur_block_count - last_block_count;
if (block_diff === 4) {
// new piece has appeared, read all the data at next frame
last_block_count = cur_block_count;
pending_piece = true;
}
else {
// assuming we aren't dropping any frame, the number of blocks only reduces when the
// line animation starts, the diff is twice the number of lines being cleared.
switch(block_diff) {
case -8:
onTetris();
case -6:
// indicate animation for triples and tetris
clear_animation_remaining_frames = 6;
last_block_count += (block_diff * 5); // equivalent to fast forward on how many blocks will have gone after the animation
break;
case -4:
if (pending_single) {
// verified single (second frame of clear animation)
clear_animation_remaining_frames = 6 - 1;
last_block_count -= 10;
}
else
{
// genuine double
clear_animation_remaining_frames = 6;
last_block_count -= 20;
}
pending_single = false;
break;
case -2:
// -2 can happen on the first clear animation frame of a single
// -2 can also happen when the piece is at the top of the field and gets rotated and is then partially off field
// to differentiate the 2, we must wait for the next frame, if it goes to -4, then it is the clear animation
pending_single = true;
break;
default:
pending_single = false;
}
}
Oh, If block count is 200, that's a game end event too.
In term of the optimization of not reading level
. Maybe do that one last? For Das Trainer, I use color references, rather than reading color 1 and color 2 from the piece stats (coz there's no piece stats in das trainer). But so to scan the field and know which colors to check against, I need the level first (bit of chicken and egg with what you mentioned above š
)
Oh right; all my suggestions were aspirational; i lost interest a long time ago due to 1) it runs fine on my box 2) no one seems to be using it except people who can already dev.
Yes that code looks good in terms of not reading letters/score every frame which cuts out 99% of the heavy lifting. Merging!
Thanks!
I'm going to try to submit a few PRs for some of the stuff I've had dormant for a while. It's quite hard to extract changesets that are nicely self contained (and will reduce my pain in resolving code conflicts later š )
So, starting with a simple one here, hopefully straight forward.
There are are some minor optimizations that can be done in OCR. If you OCR
das
with Das Trainer, values are from00
to16
, so we can introduce a digit pattern ofB
to denote[0, 1]
. PatternB
is not yet used in the code, but added anyway :)Similarly, for the vast majority of human players,
level
will not reach beyond 29. Tetris renders level >= 30 with hex characters, so to cover everything, the pattern needs to beAA
, but for most players for whom level 29 is kill screen, that's overkill to compare against. So I introduce another setT
(for triplet) for[0, 1, 2]
, such that players can use the patternTD
forlevel
.I didn't know if you'd want to keep
AA
for level as default or useTD
as default, so I made that one change in a single commit that can easily be removed if needed :)Cheers!