Currently I'm working on an application called Richtris, which will be a tool for visualizing my games. In addition to HD rendering of game elements, it will also have many additional features known from CTWC. After the game is over, detailed statistics will be visible and one value in them is to be the maxout level in the form of the level number and the fraction of the line. I am talking about this: But I don't really understand how it is calculated. @Kitaru: can you explain where this result comes from, what the formula for calculating it looks like and what is what in this formula? I would be very grateful for help in understanding this issue.

It's more or less just line intersection. Take the pre-max score + final line clear score - 999999 to get the amount of excess points above a max-out. (Excess score / final line clear score) gives you the percentage of the final line clear that overshot 999999. Or, you can work from the opposite direction using 1 - (excess score / final line clear score) to give the percentage of the final line clear that was needed to exactly reach 999999. In either case, multiply by the number of lines in the final clear by the percentage needed/overshot in order to get the fractional lines needed/overshot and either add to pre-max lines or subtract from final line count as needed. In the example given, 991000 + 34800 - 999999 = 25801 points in excess of max. 25801 / 34800 ≈ 74.14% of the final line score in excess of max, or conversely, ≈ 25.86% of final line score needed to achieve max. 4 lines in a tetris * 25.86% of a tetris needed to max = 1.03 fractional lines. (Or vice versa -- 4 lines in a tetris * 74.14% ≈ 2.97 lines overshot, and 224 total lines - 2.97 overshot = 221.03, or Level 28 + 1.03.)

@Kitaru: thank you very much for the clarification — now it is clear. I knew it was a line faction, but I didn't find a way to get "1.03" at the end, based on sample data. Ok, I prepared a function in Free Pascal to produce the final maxout result string: Code: { parameter sample meaning ALevel 28 pre-max level id AScore 991.000 pre-max score AGainLines 4 final line clear count AGainScore 34.800 final line clear score } function GetMaxOutResult(ALevel, AScore, AGainLines, AGainScore: Integer): String; var ScoreTotal, ScoreOvershot: Integer; PercentOvershot, FractionalLines: Single; begin ScoreTotal := AScore + AGainScore; // 1.025.800 points ScoreOvershot := ScoreTotal - 999999; // 25.801 points PercentOvershot := ScoreOvershot / AGainScore; // 74.14 percent FractionalLines := AGainLines - AGainLines * PercentOvershot; // 1.03 lines Result := '%.2d+%1.2f'.Format([ALevel, FractionalLines]); // 28+1.03 lines end; Simplified version, with a few auxiliary variables and comment lines so that you can see what is happening in the code. By using this function as follows (https://ideone.com/Vio13B): Code: ResultString := GetMaxOutResult(28, 991000, 4, 34800); the value returned is "28+1.03", so it seems to work correctly.

That would just be the number of lines they're at at the time, since it doesn't change on soft drop. I'd like to add a reminder that the score calculation in NES Tetris uses the level number *after* the line clear - something that is easy to forget if you can't see the score directly. Did you make sure that the formula works properly when crossing level boundaries? (I didn't see it in what you coded.)

@Arcorann: thanks for reply, especially because now I see that still the calculations in my code are wrong. For the example data provided by @Kitaru the result is correct but, if I take a data of the real game, e.g. the @niner80 record, the result is completely wrong. So thank you again. From what I see in the example from @Kitaru, the level number before reaching max out is taken into account for the calculation. Now I understand even less how it should be calculated... @Kitaru did not write about the fact that it is important for calculations, so I did not take this into account. It is not possible to write a function that calculates the result, since I do not have complete information about the input data and about cases that should be taken into account. Maybe instead of explaining the example, someone will provide an algorithm in the form of a pseudo-code or in the code of some non-functional programming language (no matter which one)? Taking into account all important cases (with maxing by push down points) and providing sample data to check the correct operation. And what about PAL version? @Kitaru — can you do this? Formula and conditional instructions are key — then everything will be clear to me.

Sorry, I may have gone a bit light on the explanation -- was aiming to answer the question at hand rather than write a complete specification. I wrongly assumed that scoring being based on level after lines are cleared was common knowledge, and thought boundary checking for level+lines notation in cases where the fractional result doesn't add up to 10+ lines would come naturally enough, but it's always better to have all the details accounted for. It's not the cleanest approach, but hopefully this bit of JavaScript specifies things well enough: Code: function _calcMax(entry) { var score = parseInt(entry['score']) if (score >= 999999) { score = 999999 var level = parseInt(entry['level']) //this is the level _after_ lines have been cleared var premaxScore = parseInt(entry['premaxScore']) var premaxLine = parseInt(entry['premaxLine']) var postmaxLine = parseInt(entry['postmaxLine']) var lines_delta = postmaxLine - premaxLine var score_delta = (level+1) * [0,40,100,300,1200][lines_delta] var scoreWithAlpha = premaxScore + score_delta var scoreExcess = scoreWithAlpha - score var lines_adjusted = lines_delta - ((scoreExcess/score_delta)*lines_delta) var postmaxLine_adjusted = premaxLine + lines_adjusted if (Math.floor(postmaxLine_adjusted/10) < Math.floor(postmaxLine/10)) { level -= 1 } while (postmaxLine_adjusted >= 10) { postmaxLine_adjusted -= 10 } var line = postmaxLine_adjusted entry['scoreWithAlpha'] = isNaN(scoreWithAlpha) ? null : scoreWithAlpha entry['level'] = isNaN(level) ? null : level entry['line'] = isNaN(line) ? null : line } return entry }

Edge case I forgot about: what happens if the player maxes out but gets both line clear and soft drop points at the same time?

@Kitaru: thank you again. I have translated your code to Free Pascal and now it works fine, returns the correct result. As a sample, I took the data from this video and the result coincides with that in the table of records. My code, translated almost 1:1 looks as follows: Code: uses Math, SysUtils; { argument sample meaning APreMaxScore - 981653 - score before final line clear APostMaxScore - 1012853 - score after final line clear APreMaxLines - 186 - lines count before final line clear APostMaxLines - 190 - lines count after final line clear APostMaxLevel - 25 - level id after final line clear Result - 24+8.35 - max out result } function GetMaxOutResult(APreMaxScore, APostMaxScore, APreMaxLines, APostMaxLines, APostMaxLevel: Integer): String; type TIntDynArray = array of Integer; var Score, Level, LinesDelta, ScoreDelta, ScoreAlpha, ScoreExcess: Integer; Lines, LinesAdjusted, PostMaxLinesAdjusted: Single; begin if APostMaxScore >= 999999 then begin Score := 999999; Level := APostMaxLevel; LinesDelta := APostMaxLines - APreMaxLines; ScoreDelta := (Level + 1) * TIntDynArray.Create(0, 40, 100, 300, 1200)[LinesDelta]; ScoreAlpha := APreMaxScore + ScoreDelta; ScoreExcess := ScoreAlpha - Score; LinesAdjusted := LinesDelta - ((ScoreExcess / ScoreDelta) * LinesDelta); PostMaxLinesAdjusted := APreMaxLines + LinesAdjusted; if Floor(PostMaxLinesAdjusted / 10) < Floor(APostMaxLines / 10) then Level -= 1; while PostMaxLinesAdjusted >= 10 do PostMaxLinesAdjusted -= 10; Lines := PostMaxLinesAdjusted; Result := '%.2d+%1.2f'.Format([Level, Lines]).Replace(',', '.'); end else Result := '-'; end; So, if the max out occured, the result is the valid string, otherwise the result is just dash character. But still this algorithm does not support maxing out by using push down points only (without line clear). In this scenario, the code above will crash at this line: Code: LinesAdjusted := LinesDelta - ((ScoreExcess / ScoreDelta) * LinesDelta); because the ScoreDelta is zero, so we have a division by zero exception (SIGFPE). What should be done in this situation and how the result should look like, if there was no line clear but the score grows to 999999+? I know that JavaScript is a different language than Free Pascal and can do different things (like silent math exception handling), but Pascal is practically the same as C/C++. So if you could answer in the context of general-purpose procedural languages I would be grateful, because I cannot translate 1:1 some JS constructions. Also I understand that maxing out using push down points only is an (edge case)³ and probably will never happen but, if such a situation occur, I don't want my software to crash. @Kitaru: can you refer to this question? Does your code handle such a case correctly?