How Block Blast harvests its gamers for advert cash · ayoships.com


ayoships.com

Rules downloaded from their server determine your items, your rating and your adverts. One waits for an costly advert, then asks for a hand you may’t place.

I posted a video about this sport that acquired over 15 million views, so I needed to do the complete technical write-up: the code, and how one can reproduce it your self.

Some context on why it issues: between January and May 2026, Block Blast made an estimated $127 million from adverts. Candy Crush Saga made $81.1 million over the identical months (Sensor Tower estimates).

Most of Block Blast isn’t within the app you put in

We began with the Android app, model 10.8.2. We decompiled the Java facet with jadx and the native Cocos library with Ghidra, and located nearly nothing fascinating within the bundle you obtain from the shop. Block Blast is constructed from small modules the code calls traits, and every trait has variants known as arms. Running it on a clear Android 14 emulator with no account and watching its cache and logcat confirmed the way it works: when the app launches, the server sends a config that claims which arm of which trait you get, and the app downloads them as encrypted JavaScript. The config my set up acquired names about 700 traits. My set up chosen 254 of them, and 101 had been downloaded after set up.

CTAgloReviveTraitCTReviveTimeOutSkipSplashTraitClassNewDeathOptimizeTraitLethalToHardTraitLowerReviveUIShowByLimitHighScoreTraitDifferentStrategyDieTraitEndlessDeathBoardTraitFastHandPunishTraitGLBFDieReplayTraitIsCacheReviveNumTraitIsClassADReviveNoInerestingTraitIsOpenInAdvanceDeathByLowEfficiencyGiveReviveTraitIsOpenRandomToRandomNoDieTraitStageReviveCondiTraitStageReviveCountTraitStageReviveTraitMABXiBaiTraitMultiClearBoostThenKillTraitRepateCPlusPlusRandomNoDeathTraitReviveBuffTraitReviveCleanBoardTraitReviveCountTraitReviveLimitTraitRewardHighValueAdHarvestTraitAdLevelTraitAdPlusMinusStrategyTraitAddClearTipsExTraitAddMoreGameTraitAdjustPercentChTraitAlgoFillSortEdgeTraitAlgoSDK_modifyBlockPosTraitAlgoSDK_useAlgoSoAloneTraitAlgoSameBlockShangZengTraitAlgoSymmetryBlockTraitBComboScoreChangeTraitBCommentDisplayTraitBPuzzleTimeTraitBReplaceBinaryTraitBackSpaceClickLimitTraitBaseplateConditionDiffTraitBaseplateLaneTraitBaseplateTraitBlockLimitClassicsTraitBlockLimitLevelTraitBlockPoolRoundAdjustV2TraitBlockPoolStrategyExperimentTraitButtonClickAddShakeTraitCTAdvertisementMultipleCallTraitCTAdvertisementNoHideTraitCTAgloNameSpecialTraitCTAgloNameTraitCTAgloStrategyTraitCTBAlgorithmGaohuibaoARGTraitCTChapterScoreComboTraitCTChapterSkinBonesTraitCTChapterWinOverTraitCTClassSkinBonesTraitCTComboDotTraitCTCommentskinWithFeedBackTraitCTDotClickGameEndInfoTraitCTDotClickUsrDataGameEndTraitCTDotDeadReportToUsrDataGameEndTraitCTDotFirstClickDataGameEndTraitCTEffectAccTraitTraitCTGBMClearTraitCTGBMEmptySurfaceGameStartDotTraitCTGameComboScoreTraitCTGameEndStatusDotTraitCTLowDeviceEffectTraitCTMergeBlocksAgloNameTraitCTMergeBlocksSkinBonesTraitCTSkinBonesTraitChapterAlgoStrategyTraitChapterCollectScoreFixedTraitChapterWinShowRainbowTraitClassGuideEndNoAwaitNextFrameTraitClassicsRandomReplaceTraitCleanSceneRandomSkinTraitCleanSceneUseSequenceSkinTraitClearBoardHeartRippleAniTraitClearBoardPlusBlockPosTraitClearBoardPlusTraitComboAddRemoveNumTraitComboModeCtrlTraitComboRainbowTraitComboScoreSplitTraitComboTimeTraitCombo_tips_lv_blockTraitComboflyScoreTraitConsecutiveLossRecoveryTraitContinueSameMoreRoundLimitTraitControlHighestScoreRhythmTraitCrossClearHardTraitCtrlAlgoLimitTimeTraitDailyPerformanceReviewTraitDailyWinStreakStateTraitToughChangeColorTraitEightHoursTravelTraitExceedAimScoreTraitExpendBoardScoreTraitFastNearTkxcClassTraitFastNearTkxcTraitFeatclearscreenbombTraitFeatclearscreenbombfixTraitFillCleanAlgoTraitFillExcludeTraitFirstGameScoreTraitFirstHardMoreClearAlgoTraitFiveRoundStatisticsTraitGLBFMiddleTipsViewTraitGLBFProgressTraitGLGameAdStrategyOptimizeTraitGLResultView6TraitGLShowAdsByGameTimeTraitSportInfoDisplayTraitGameLobbyNewEntryCtrlTraitGameLobbyTraitGameLobbyUnlockByBreakHighScoreTraitGameOverShowMiniGameIconTraitGateWinStreakStateTraitGpEmptyBoardRatioGuardTraitHighProgressShowBannerTraitHighScoreBreakthroughBoostTraitHighScoreLimitByUserAbilityTraitHighValueInterstitialReliefTraitExcessiveWeightSessionQuestionReplacerTraitHoleFillPriorityTraitHotAreaAdaptScreenBottomStateTraitHotAreaClickUtilTraitInitBoardDelayTraitInitBoardSnakeProStyleProTraitInitialBoardDisableTraitIsOpenBottomCoverChangeTraitIsOpenChangeSkinTraitIsOpenClassEncourageEffectByScoreTraitIsOpenComboContinuousTraitIsOpenComboInterruptionStrategyReplaceBlockTraitIsOpenExtraReportTraitIsOpenGLHallMoreGamesPopupTraitIsOpenGlLoadAllSportBackTraitIsOpenLevelOverTipsChangeTraitIsOpenMoreCleanChangeTraitIsOpenMoreCleanTraitIsOpenNoVideoNoRevivieTraitIsOpenOperaPosTraitIsOpenRainbowPutBlocksEffectTraitIsOpenRightPutTraitIsOpenShang1ToShang3TraitIsOpenVibrateTraitIsOpenZhiJueToKunNanTraitIsOpenZiMuShangZengTraitIsPuzzleTimeTraitIsShowAchievementTraitIsStatisticalDataCollectTraitIsTravelMoreDiamondAwardTraitIsTurnAroundSurfaceTraitIsWarmhandsTraitJewelNewOutBlockTraitLDEnableNoSyncHighScoreRollingTraitLWMDualConditionHardPuzzleTraitLaneSchemeTraitLevelConfigTraitLevelExpAlgorithmTraitLevelLoopTraitStageNoThemeTraitLevelRandomReplaceTraitLevelRemoteThemeTraitLevelWayHelpConfigTraitLevelWayHelpTraitLimitHighComboTraitRestrictNoviceBlockSizeTraitLineDynamicSendTraitLoadingTieringTraitNativeThemeSaveTraitLoginTimeFirstDifficultyRatioTraitLongThinkClearAssistTraitLowFeedbackPleasurableOfferTraitLowHardSetFps30TraitLowWeightFillBlockSortAdjustAlgoTraitLowWeightFillBlockSortTraitMaturityDevelopmentV2TraitMergeBlocksIsPuzzleTimeTraitMergeBlocksLaneSchemeTraitMoreClearStopComboTraitMoreTimeToOpreateStateTraitMultiElementCollectTraitNewAbilityModelTraitNewBottomOfferTraitNewPlayerContinuePlayIncentiveTraitNewSeasonNotifyTraitNewStructTraitNewUserLevelTraitNew_player_direct_guide_to_thirdTraitNewbieScoreRegulationTraitNoBannerAdNewConsumerByDaysTraitOpenChangeContinueHardTraitOpenComboModelTraitOpenLevelModelTraitOriginSkinShowGreyDefultSetBtnTraitOverHighScoreClearBoardTraitParallelClearHardTraitParam230012TraitPixelBitControllerTraitPlaceBackAdaptTraitPlaceHabitAdaptPathTraitPlaytimeNoInterstitial1TraitPostGameToServerTraitPreClearScreenAnimOptimizeTraitDownsideIntervalTimeRandomBetaTraitPromotionMoreGamesInBlockCtrlTraitPutBlockOptimizeTraitPuzzle100TraitPuzzle100WeightConditionTraitPuzzleAfterTimingTraitPuzzleBeforeTimingTraitPuzzleConditionDiffTraitPuzzleFail100TraitPuzzleSuccess100TraitPuzzleTiming100TraitPuzzleTimingMaxPriorityTraitRatioAdjustTraitRatioDownTraitReduceTravelSettlementTraitReplaceBoardTraitReplaceClearnScreenAniTraitRightPutHintCtrTraitRobotModelEventDataTraitSequencePredictDynamicDifficultyTraitServerControl_board_init_v2TraitSetAddDefultSkinBtnTraitSettleFireworkTraitSetupNewTraitShangZengAddRandomTraitShangzeng3ReplaceZimushangzengTraitShowSSSWhenPores and skinChangeTraitShowTravellClearAnimaTraitSimpleMultiSolutionTuning2TraitPores and skinChangeAblePushTraitSkinPreEliminateGoodPerformanceTraitSkinRandomTraitSkinThree24NewColorsTraitSkinThreeOptimizationsTraitSkinThreeStageTraitSlowPlayerAnimTraitSmallgameLocalResTraitSolidColorTraitTerminateRefreshScoreTraitTheMoreEliminateTheMoreGoodEffectTraitTravelEntryClockShadowFixTraitTravelEntryClockShadowTraitTravelHappyOverTraitJourneyNoSkinTraitTravelResultOptimizingTraitTravelScoreCutoffByProgressTrait

All 254 traits assigned to my Android take a look at set up. The ones about shedding, revives and adverts are highlighted.

So two folks enjoying facet by facet may be working completely different video games, and the studio can change yours with out an app replace. Most of these 254 are extraordinary: tutorials, animations, sounds. A handful are about while you lose. One of them is about adverts.

The rule that waits for an costly advert

It’s known as RewardHighValueAdHarvestTrait. By discovering the place the sport unlocks these information in its personal native library, we decoded it into 15,616 bytes of plain, minified JavaScript. Its constructor units the parameters:

RewardHighValueAdHarvestTrait.js · traces 63–67
t.HIGH_PRICE_MULTIPLIER = 3.5;
t.MEDIUM_PRICE_MULTIPLIER = 1.5;
t.MIN_HISTORY_AD_WATCH_COUNT = 3;
t.RECENT_AD_NO_ESCAPE_COUNT = 3;
t.TARGET_AD_LEVEL = "excessive";

It waits for 3 issues to line up: you completed your final three rewarded adverts, the advert loaded proper now’s price at the least 3.5 occasions your final one, and also you’re 1,000 factors previous your excessive rating. All three get checked in a single operate. Here it’s with the minified null checks rewritten as non-compulsory chaining; the logic is similar:

shouldTriggerDeath · traces 262–267 (reformatted)
shouldTriggerDeath() {
  if (!this.canTrigger()) return false;                  // completed your final 3 rewarded adverts
  const d = this.storageData;
  return d.currentGameNum === hs.classGameInfo?.gameNum   // similar sport
      && !d.currentGameHasTriggeredDeath                 // hasn't fired but
      && !(hs.classScoreInfo?.rating < this.currentScoreThreshold)  // previous excessive rating + 1,000
      && !!this.isTargetAdPrepared();                       // loaded advert ≥ 3.5× the final one
}

Here’s the way it checks every one.

1. You completed your final three rewarded adverts. Every time a rewarded advert begins, the rule pushes false onto an inventory and retains the newest three (traces 174–183). If the advert SDK stories that the reward was earned, it flips that entry to true (191–199). The examine wants all three to be precisely true:

canTrigger · traces 268–275
t.prototype.canTrigger = operate() {
var e = this.storageData;
if (e.totalRewardShowCount < this.MIN_HISTORY_AD_WATCH_COUNT) return !1;
var t = e.recentRewardResults.slice(0, this.RECENT_AD_NO_ESCAPE_COUNT);
return !(t.size < this.RECENT_AD_NO_ESCAPE_COUNT) && t.each(operate(e) {
return !0 === e;
});
};

That flag comes from AppLovin MAX’s onUserRewarded callback within the Java layer, so an advert you shut early stays false.

2. The advert loaded proper now’s price at the least 3.5 occasions your final one. The rule reads the worth of the most effective rewarded advert at the moment loaded with NativeAd.callNativeGetMaxEcpmNumber("2") (line 304). In the Java layer, that decision goes by way of every prepared rewarded advert slot, takes the proceeds AppLovin MAX stories for it, multiplies it by 1,000, and returns the very best. The worth of your final rewarded advert will get the identical ×1,000 (line 310), so the comparability is like for like:

classifyLoadedRewardLevel · traces 290–293
t.prototype.classifyLoadedRewardLevel = operate(e) ;

3. You’re previous your outdated excessive rating. At the beginning of every sport the rule saves your excessive rating (line 145). The bar is that top rating plus scoreDeltaStep (traces 107–110). Each participant will get one arm: mine, 332243014, makes use of 1,000, and the app’s bundled config has others at 500 and 750. If you revive after the rule fires, the bar strikes up one other step, so it may hearth once more in the identical sport.

Then it asks for the deadly puzzle. Before dealing your subsequent set, the sport runs an inventory of “backside provide” guidelines so as. When this one qualifies, it replaces the checklist with a single generator:

onBottomOfferAlgo · traces 245–251
t.prototype.onBottomOfferAlgo = operate() {
if (this.shouldTriggerDeath()) {
hs.algorithmBottomSequenceInfo.setAlgorithmBottomList([ this.deathAlgorithm ]);
this.storageData.currentGameHasTriggeredDeath = !0;
this.saveStorageData();
}
};

loss of lifeAlgorithm returns hs.OFFER_TYPE.ZHI_SI_TI (line 102). The sport sends that request to its puzzle engine and offers the three piece IDs it will get again (predominant script traces 45062–45121 and 15074–15123). If the engine fails to discover a set, the rule resets and tries once more on the subsequent deal.

To watch it determine, we ran the downloaded file itself, unmodified, with take a look at advert and rating values. It requests the deadly puzzle precisely when all three situations maintain: 28 instances, together with two adverts as an alternative of three, one advert closed early, a rating 1 level brief, and an advert price just below 3.5×.

Then we ran it dwell, in a take a look at copy of the iPhone app working in Apple’s simulator, and browse the rule’s inputs whereas we performed. It had three good rewarded adverts on file. The rating was previous the road. A rewarded advert from Google AdMob was loaded and prepared. The rule priced it at 72.39, towards 37.41 for the final one, which is 1.94 occasions. It wants 3.5. So on iPhone the rule is dwell, studying actual advert costs, and ready for an advert price 3.5 occasions your final one.

The deadly puzzle

The identify maps by way of three tables in the primary script:

predominant/index.45fec.js (unique line numbers)
23878  e[e.ZHI_SI_TI = 9] = "ZHI_SI_TI";
16092  … r[n.OFFER_TYPE.ZHI_SI_TI] = i.AlgoClassTypeConst.algo_must_death, …
258441 e[e.algo_must_death = 24001] = "algo_must_death";

Algorithm 24001 exists twice: in JavaScript, and within the sport’s native C++ library, the place the operate is known as func_death_blcok (their spelling). They do the identical search. Here’s the JavaScript core:

rC · predominant/index.45fec.js traces 318602–318678 (excerpt)
for (var d = 0; d < n.size; d++) if (41 != d) {
var m = n[d];
r.canPut(m) ? o.push(m) : i.push(m);   // o: matches now, i: does not match now
}
…
iC(r, o[p], o[f], i[_], h);               // strive each order of two items that match
iC(r, o[p], i[_], o[f], h);               // and one that does not, clearing traces
iC(r, o[f], o[p], i[_], h);               // after every placement
iC(r, o[f], i[_], o[p], h);
if (0 == h.size) {                     // no order labored: take this set
u = [ o[p], o[f], i[_] ];
…
a.names = [ "致死题", "致死题", "致死题" ];   // "deadly puzzle" ×3

It appears to be like for 2 items that match now and one which doesn’t, and retains the set provided that no order and no set of positions locations all three, counting each line you’d clear alongside the way in which. Two of the three match once they’re dealt, so the hand doesn’t look unattainable till you strive it.

Here’s what that appears like, with the sport’s personal code working in your browser. Step by way of it:


Every step comes from working the sport’s personal generator code in your browser.

Press “New random board” for a contemporary mid-game board, and the sport’s code picks a brand new hand for it. On the take a look at board within the proof bundle, the sport’s generator accepts a vertical domino, a horizontal domino and a 3×3 sq. as a deadly hand: we tried all 171 placements, and none of them end it. On an empty board, those self same three items match 67,680 methods in every order.

The advert rule isn’t the one one which asks for it

The deadly puzzle is a generator, and any rule can ask for it. The advert rule is one caller. While we had been testing it dwell on iPhone, a second caller fired on digicam: PersonalMarginBreakTrait. Once you’re 10% previous your excessive rating, it rolls on each deal, and 20% of the time it asks for a similar deadly puzzle:

PersonalMarginBreakTrait · onAlgorithmStrategyPriority, traces 153–159 · dwell config: prob 20, recordPercent 10, replaceAlgoId 9
t.prototype.onAlgorithmStrategyPriority = operate() {
this.hasReplacedThisSport = !1;
if (this.state.isFirstSport) ; else if (this.isReviveSpherical()) ; else if (this.state.isBreakHighScore && this.isCurrentScoreOverRecordPercent()) if (100 * Math.random() >= this.prob) ; else {
var e = this.replaceAlgoId;   // 9, the deadly puzzle
…
if (this.nameNewAlgorithm(e)) ; else {

Our file was 1,304, so its line was 1,434. At 3,356 factors it rolled, and the sport dealt a five-long bar, a five-tall bar and a zigzag. The sport’s personal file of that deal names this rule because the caller, reveals algorithm 9 each requested and used, and labels all three items 致死题, deadly puzzle. There was no solution to place them. Two separate exhaustive searches agree: zero. The rule, the dwell settings and the deal are within the live iPhone bundle.

The mannequin that picks the place you lose

The guidelines to date are hand-written. The sport additionally downloads educated fashions and runs them in your telephone, and one in all them decides the place in a stage the deadly puzzle reveals up.

It’s a 500-tree XGBoost classifier for the sport’s stage mode, which the code calls Chapter. Its file is known as predict_travel_difficulty_dead, and its 52 inputs are principally about the way you’ve been doing: your present stage, what number of occasions you’ve tried it, what number of video games you’ve completed this week, and the way lengthy you’ve had the app. The sport feeds it 12 choices, 4 values of a setting known as sw_process occasions three issue ranges, and retains whichever one the mannequin scores highest:

ClientDieModelLoadTrait · doPredictFeature, traces 1286–1312 (excerpt)
if (0 == e) for (var i = 1; i <= 4; i++) for (var l = 1; l <= 3; l++) {
var c = this.getInputFeature(this._jsonModel.learner.feature_names, i, l);
if ((u = this.xgb_clf_predict(c, …)) > n) {   // preserve the highest-scoring choice
n = u;
a = i;
o = l;
}
}
…
storage.setItem("DieModel_last_predict_info", { swProcess: a, travelLvDifficulty: o, pre: n });

Another rule turns that choose right into a stretch of the extent: the primary 30%, 30 to 80%, or the final 20%. When you attain that stretch, it provides you an additional revive should you often take them (greater than 75% of the time). Then, from the fourth deal after that, it asks for the deadly puzzle:

IsOpenDeathChangeReviveCountTrait · traces 224–229 and 181–184
var t = hs.chapterConfigInfo.getChapterProgress(), i = e.getLastPredictInfo(this.traitName), r = i.swProcess, …;
…
1 == r ? s = [ 0, .3 ] : 2 == r ? s = [ .3, .8 ] : 3 == r ? s = [ .8, 1 ] : 4 == r && (s = [ 1, -1 ]);
return t < s[0] ? a.First : t <= s[1] ? a.Second : a.Third;   // earlier than, inside or after that stretch
…
if ((n = this.saveData).needZs && n.zsRoundNum > 3) {
hs.algorithmStrategyInfo.setAlgorithmPriorityList([ hs.OFFER_TYPE.ZHI_SI_TI ]);   // the deadly puzzle
hs.algorithmStrategyInfo.setAlgorithmFailList([ hs.OFFER_TYPE.SHANG_ZENG_3 ]);
}

Other guidelines learn the identical choose the alternative means and make a stage simpler when it lands on the fourth choice.

The mannequin that costs you

A second mannequin places a worth on you. The sport downloads xgboost_regression_model111.json from its server and runs it in your telephone.

It’s an XGBoost regression mannequin: 59 determination bushes, as much as 8 ranges deep, 27 inputs. Most of it’s about cash: 18 of the inputs are in regards to the adverts the sport has loaded for you, at present and yesterday. The relaxation are the place you’re, your telephone, while you put in, and the way your first sport went. The bushes lean hardest in your first rating, the value of your newest advert, your RAM, your telephone mannequin and your nation.

The code calls its reply finalEcpm. eCPM is the advert trade’s identify for what a thousand adverts are price, and it’s the identical unit the sport shops its advert costs in. So the mannequin is estimating what you’re price in adverts.

GPT-6 Astra ported the sport’s enter code and tree stroll, and the port reproduces the sport’s personal outputs to the final decimal. We ran it on a made-up new participant within the US with a primary rating of 1,000, as soon as on an iPhone 15 put in from the App Store and as soon as on Android, then modified one enter at a time:

Change solely this Ad worth, iPhone 15 Ad worth, Android
Nothing (US, first rating 1,000) 25.48 48.96
Live in Brazil as an alternative 8.08 17.90
Live in India as an alternative 5.59 14.83
First rating of 500 25.79 53.57
First rating of two,000 21.12 52.64
First rating of 10,000 20.34 58.26

Moving the identical participant from the US to Brazil cuts their worth to a few third, and India cuts it additional. Your first sport issues too, however not in a straight line: on iPhone, a primary rating of 500 makes you price greater than 2,000 or 10,000.

The sport makes use of costs like this to determine what sort of adverts you get, and what number of. The clearest case asks its server for 2 costs for you, one for full-screen adverts and one for rewarded adverts, and compares them:

IsOpenInterAndRewardAdvDecideReviveTrait · checkEcpm, mainTraits/index.88dc4.js traces 38529–38534
t.prototype.checkEcpm = operate() {
var e, t, o, r = this.saveData.interEcpm, i = this.saveData.rewardEcpm;   // your full-screen and rewarded costs
…
return (…isReverse) ? r <= i * (…a number of) : i >= r * (…a number of);
};

In the variations the app ships, in case your rewarded adverts are price at the least twice your full-screen adverts (1.5 or 2.5 occasions in different variations), you get an additional likelihood to revive, and a revive performs a rewarded advert. If full-screen adverts pay extra for you, you don’t.

Other guidelines work the identical means. FullScreenAdAddByEcpmTrait provides an additional full-screen advert when your worth is excessive. IsOpenEcpmTooLowNoInterTrait skips full-screen adverts while you’re price nearly nothing. Just a few extra open an early or a 3rd revive when your rewarded adverts are price sufficient.

Try the on-phone mannequin your self:

25.48
Your advert worth
per 1,000 adverts (eCPM)

finalEcpm 25.47779944720001
0/59 bushes moved

Here’s the a part of the sport that walks the bushes. It’s a plain loop: begin on the root, go left or proper on every enter, add up the leaf weights.

EcpmFeatureBaseInfo · runModel, predominant/index.45fec.js traces 63141–63175 (excerpt)
for (var m = parseFloat(a + ""), u = 0; u < t.size; u++) {   // m begins on the base rating; t is the 59 bushes
for (var p = t[u], f = 0, _ = 0; _ < o; ) {
_ += 1;
var h = p.left_children[f], g = p.right_children[f], y = p.split_conditions[f], C = p.default_left[f], v = s[e[p.split_indices[f]]], T = -1;
(null === v && 1 === C || v < y) && (T = h);
(null === v && 1 !== C || v >= y) && (T = g);
if (-1 === T) {
m += parseFloat(p.base_weights[f].toString());        // reached a leaf: add its weight
break;
}
f = T;
}
if (_ >= o) throw new Error("[EcpmFeature] 树的遍历超过最大深度,可能存在死循环。");
// "Tree traversal exceeded max depth, doable infinite loop."
}

A mannequin that guesses you’ll play much less tomorrow

There’s additionally a 1,000-tree LightGBM mannequin with 93 inputs that predicts whether or not you’ll be much less lively tomorrow. It leans hardest on how lengthy you’ve had the app, your excessive rating, your RAM and your common rating. If it scores you at 0.69 or above, a companion rule swaps in a pink revive button, and the revive skips the advert:

NewNextDayLowActivityTrait line 1010 · NextDayLowActivityFirstReviveFreeTrait traces 112–117
return this.state.predictionScore >= this._lowActivityThreshold;   // 0.69
…
if (hs.tp.isClassAdvertisement_RewardProxyShieldPlayAdvertisement(e) || …) 

It works as soon as a day.

Models on their server

Some guidelines ship your information out and act on the rating that comes again, from server fashions with names like churn, LTV, revive-click and xDeepFM. Their weights keep on the server, so all we are able to present is what the telephone sends. One of these requests, to /v1/ecpm_prediction, sends 40 fields, together with your service, storage, the hour and stats in your current advert costs.

Other guidelines that form your sport

The advert rule isn’t the one one which modifications your items or your rating. We caught one working dwell on iPhone. MaturityDevelopmentV2Trait provides every session a “maturity” rating product of three elements: behavior (10.98), ability (14.66) and your advert historical past (19.27). Ours added as much as 44.91, below its line of 65, so it swapped a few of the exhausting puzzles it could have dealt for simple fills. Another, FailStreakStrategyTrait, stepped in after two video games beneath our file and requested for simpler items.

These got here up whereas tracing the advert rule on Android, and every has the identical sort of proof in my analysis information.

Rule What the code does
MultiClearBoostThenKillTrait Sends your person ID to 2 server endpoints and will get again two labels. The code names each tiers as “not delicate to being compelled to die” (怼死不敏感). For these tiers, it waits for a giant clear, then requests the deadly puzzle a couple of rounds later as soon as the sport has lasted about so long as your traditional sport.
MABXiBaiTrait A six-arm experiment on how quickly the deadly puzzle may be requested: by no means, or after 4, 6, 10, about 17 or half-hour. For every arm it stories whether or not you began a brand new sport inside quarter-hour, and what share of revive provides you accepted.
FastHandPunishTrait Asks for exhausting items when your final six units averaged below 4.2 seconds every and also you’re previous 93% of your excessive rating.
AdPlusMinusStrategyTrait After one sort of further revive provide, provides a random bonus of as much as 10% of your excessive rating to your present rating.

Questions folks requested

Is it the identical on iPhone?

Yes. We took aside Block Blast for iPhone too, model 7.4.9, and a second GPT-6 Astra run checked these findings from scratch. It downloads the identical rule from its personal replace server, and the file matches the Android one aside from one line. Everything else is equivalent: three adverts, 3.5×, the rating bar, and the identical lethal-puzzle generator.

The iPhone app additionally lists a second model of the rule, RewardHighValueAdHarvestV2Trait, and in that one the ad-history examine may be switched off:

RewardHighValueAdHarvestV2Trait.js (iOS) · traces 323–331
t.prototype.canTrigger = operate() {
var e = this.storageData, t = this.viewCountThreshold;
if (e.totalRewardShowCount < t) return !1;
if (!this.requireRecentNoEscape) return !0;
var r = e.recentRewardResults.slice(0, t);
return !(r.size < t) && r.each(operate(e) {
return !0 === e;
});
};

The app ships 12 variations of it. Half flip that examine off, and so they want wherever from 1 to three adverts, so in a few of them a single rewarded advert you closed early is sufficient. The worth bar relies on how lengthy you’ve had the app: 3.2× the primary advert loaded that session in your first week, 2.7× by way of day 30, and three× your final advert after that.

What occurs if I play with out wifi?

This rule is saved in your telephone after it downloads, so it nonetheless runs offline. But it solely fires when a rewarded advert is loaded, and adverts load over the web, so as soon as nothing is loaded it may’t hearth. Other guidelines preserve going. On my Android set up, the one which asks for exhausting items while you’re enjoying quick close to your file runs totally in your telephone. The one which asks the server which gamers “don’t thoughts being compelled to die” can’t get a solution offline, so it stays off for that session.

Does an advert blocker do something?

It relies on the blocker. Browser advert blockers, on iPhone or Android, solely work contained in the browser, in order that they don’t contact the sport. A blocker that covers your complete telephone, like Private DNS on Android or a DNS or VPN-based blocker on both, can cease rewarded adverts from loading, after which this rule can’t hearth, for a similar motive as enjoying offline. The guidelines that don’t use adverts preserve working: the fast-play one, the experiment that occasions when the deadly puzzle may be requested, and, because you’re nonetheless on-line, the one which asks the server about you.

Is this why I at all times lose proper after beating my excessive rating?

That’s precisely the place these guidelines are aimed. We caught PersonalMarginBreakTrait doing it dwell: when you’re 10% previous your file, each deal has a 20% likelihood of being the deadly puzzle. The advert rule waits for 1,000 factors previous your file (500 or 750 in different variations), and after a revive it strikes the bar up and might hearth once more.

Can I keep away from it?

Not actually. On Android, the unique rule wants your final three rewarded adverts to pay out, so closing one early does flip it off till you’ve had three good ones in a row once more. But on iPhone, the V2 variations can skip that examine fully and hearth after a single advert, even one you closed. And the opposite guidelines that ask for the deadly puzzle, just like the timing experiment and the one which waits for a giant clear, don’t have a look at your adverts in any respect. The one factor that stops this rule in all places just isn’t having a rewarded advert loaded, which is what occurs offline or with a phone-wide advert blocker.

Isn’t this simply adaptive issue?

Adaptive issue modifications the items based mostly on the way you’re enjoying. This rule doesn’t have a look at how nicely you’re inserting items. Its inputs are your current advert outcomes, the value of the advert sitting in reminiscence, and your rating relative to your file. And the generator it asks for isn’t a tougher set of items. It’s a set chosen as a result of no means of inserting it really works.

Is this even allowed? Doesn’t Apple evaluation apps?

Apple critiques the app you obtain from the shop, and this rule isn’t in it. The app downloads it from the sport’s server after you put in, and the server decides which gamers get which model. Apple does let apps obtain JavaScript updates like this, which is how instruments like Expo Updates work, so long as the replace doesn’t change what the app is. Whether altering your items based mostly on advert costs matches that’s as much as Apple.

Why does it run on the telephone as an alternative of their servers?

The server does determine who will get the rule and which model. But the choice itself wants issues solely your telephone is aware of in that second: your board, the subsequent set it’s about to deal, and the advert that’s loaded proper now. So that half runs on the telephone, which can also be why it might be taken aside.

How I do know that is actual

  • The apps. Android: the ten.8.2 XAPK from APKPure, downloaded October 11, 2026. All its cut up APKs cross signature verification, and the model matches the general public itemizing on APKMirror. iOS: the 7.4.9 IPA from a public mirror. 4,670 of its sport sources, together with the primary script and the catalog, try towards Apple’s code signature.
  • The guidelines got here from their servers. The Android app’s cache says it downloaded the rule from bband1.afafb.com, the identical host it fetches all its traits from. We fetched that URL individually, and the file matched the app’s cached copy byte for byte. The iOS app’s signed catalog names each iOS guidelines, and the app fetches them from bbios1.afafb.com. As of October 15 all three URLs nonetheless serve the identical information, so you may examine this your self (instructions beneath).
  • It was assigned and loaded. On a contemporary Android 14 emulator with no account, at 23:01:15 UTC on October 11, the app had the config prepared, arm 332243014 chosen for this rule, the module loaded with its storage initialized, and the bottom-offer pipeline it plugs into switched on.
  • The decoding is actual. The traits are encrypted with XXTEA and compressed with Brotli. The decoded rule’s hash is beneath. For the native library, my offline decode matched a reminiscence dump of the working app throughout 25,317,680 bytes with zero mismatches.
  • Nothing within the Android sport was modified. The solely change was a rendering workaround so the sport would draw on the emulator. No scores, adverts, flags or config values had been edited.

Check it your self

There are three proof bundles: Android (113 KB, SHA-256 bd55151e6a1527f628459c2fffa115d5b14a765a3fb5a3f729387fbac6f707c8) and iOS (2.7 MB, SHA-256 02bdb0e0e9b02d77844efbcf4b82f48e17f2b688fc48cbf84f83e75ae289df7d), plus a 3rd for the live iPhone catch (270 KB, SHA-256 5f36863daabd2b500c8062d005f41e6c5e7dcea5a238c89e558e19a928dcc57f). They have the decoded guidelines, the encrypted originals, excerpts from the remainder of every app with their unique line numbers, the take a look at scripts and the provenance information. You want Node.js and Python 3.

Terminal · Android bundle
# the rule, run unmodified with pretend inputs: 28 checks, prints "handed": true
node proof/verify_reward_harvest.js

# brute-force search of the take a look at board: 0 options in each order
python3 proof/verify_unsolvable.py

# the sport's personal generator on that board: picks items [2, 3, 13]
node proof/verify_packaged_algorithm.js
Terminal · iOS bundle
# each iOS guidelines, the generator, two solvers and the code-signature examine
python3 proof/run_all.py
Terminal · the encrypted guidelines on their servers match the bundles
curl -s https://bband1.afafb.com/update/block_android_online/remoteRes/0/remote/RewardHighValueAdHarvestTrait/index.700f5.js | shasum -a 256
# dec91aa4001f651a74bf101d512587b9a364cb71ea2a81a9cc9dbbcd2444b477
curl -s https://bbios1.afafb.com/update/block_ios_online/remoteRes/0/remote/RewardHighValueAdHarvestTrait/index.ff35e.js | shasum -a 256
# 61ac70045995f44b3c6bb22b2d062a386593ce6a632fbcec3863a51110850cc2
curl -s https://bbios1.afafb.com/update/block_ios_online/remoteRes/0/remote/RewardHighValueAdHarvestV2Trait/index.677f6.js | shasum -a 256
# e38953430980f4b6a10b29337ec9a41634bb732ee84dd5997bfe78507b26ee1e
Android base APK a520fcc1d1ba1ff0c643e30b059c87a01fde650ec46be5c85fde5d815ec82447
Android XAPK 91988cf6e92cd0d346b869df0a8c6740f3a5fd8b722e21779e045426c7553015
Android rule, decoded f94a5d54d7f6ab0af097f408bda09b091e7767b17749f8ba828b41b638b330ad
Android predominant script 8dce6532cafc25d982f2af3659647e0e0017c3902cbc31f872d375804dc3678c
iOS IPA 7fc9b47e10dc8303f662e1c0ad88b93e7119e2846972eddb1a18bd205b54056a
iOS rule, decoded 179bf50ae3c0c4a9bd5457281e4876faae728778f6181dbb0d3799b42ec70d61

The key and the steps for getting previous the apps’ safety aren’t within the bundles. If you’re a journalist or researcher and wish to confirm the decoding step, email me.



Source link