さて、前回までの記事で3×3×3盤面の基本特性とコーナーロック評価関数について掘り下げたわけですが、今回は3×3×3の緊張感を一段引き上げる「HARDモード」の内部アルゴリズムと攻略戦術について解説していきます。

3×3×3のCLASSICモードに慣れてくると、空きマスの余裕さえ管理していれば安定して4096から8192を狙えるようになります。そこで「単調な定石通りに運ばせないためのスパイス」としてjs/game-core.jsに組み込んだのが、コンボ依存のスポーン数変動、合体不能な障害物「GLITCH(#)ブロック」、そして合体回数に応じてチャージされる「Multi-UNDO」の3要素なわけです。

01. コンボが途切れた瞬間に牙を剥く「期待値1.2個 vs 確定2個」のスポーン制御

まずはHARDモードで最も重要な「1ターンあたりに何個のブロックが湧くか」という計算式から見ていきましょう。js/game-core.jsのspawnTilesForMove()メソッド内では、モードごとの新規スポーン数が次のように分岐しています。

// js/game-core.js : spawnTilesForMove() 内の新規スポーン数決定ロジック
if (this.gameType === 'hard') {
  const count = (this.comboCount === 0 || Math.random() < 0.20) ? 2 : 1;
  for (let i = 0; i < count; i++) {
    const t = this.spawnTile();
    if (t) spawned.push(t);
  }
}

この条件式(this.comboCount === 0 || Math.random() < 0.20) ? 2 : 1がHARDモードの難易度を決定づけています。挙動を場合分けすると以下の通りです。

  • そのターンに1組以上マージが発生した場合(this.comboCount >= 1):80%の確率で1個スポーン、20%の確率で2個スポーンとなります。つまり1ターンあたりの平均スポーン期待値は1.2個です。1ターンに1組マージ(タイル-1個)していれば、盤面の純増期待値は-1 + 1.2 = +0.2個に抑えられます。
  • そのターンに1組もマージが発生しなかった場合(this.comboCount === 0):乱数判定すら行われず、100%確定で2個スポーンします。合体によるタイル減少がゼロのまま2個追加されるため、たった1回の「空振りスライド(整列のみの手)」で空きマスが一気に2つ消滅するわけです。

要するに、CLASSICモードなら「とりあえず端に寄せてから次の手を考えよう」という空振りスライド(純増+1)が許されますが、HARDモードで空振りスライドを連発すると毎ターン2個ずつブロックが増殖し、27マスの盤面はあっという間にパンクします。「毎ターン必ずどこかで1組以上のマージを成立させ、コンボを切らさないこと」がHARDモードの第一原則となるわけですよ。

02. 12%の確率で混入する障害物「GLITCH(#)ブロック」の内部表現

さらにHARDモードを厄介にしているのが、新規スポーン時に一定確率で混入する紫色のノイズブロック「GLITCH(#)」です。spawnTile()メソッドの実装を見てみましょう。

// js/game-core.js : spawnTile() 内のGLITCH生成判定
if (this.gameType === 'hard') {
  if (Math.random() < 0.12) {
    type = 'glitch';
    value = 0;
  } else {
    const r = Math.random();
    value = r < 0.75 ? 2 : (r < 0.95 ? 4 : 8);
  }
}

GLITCHブロックの仕様を整理すると、次の3つの特徴を持っています。

  1. 出現率はスポーン枠ごとに12%:新規ブロックが湧くたびに12%の確率で通常の数字の代わりにGLITCHブロック(内部値value = 0, type = 'glitch')が出現します。残りの88%の通常枠では「2が75%、4が20%、8が5%」の比率で出現します。
  2. 重力移動はするが、単体では絶対にマージしない:スワイプ操作を行うと通常の数字ブロックと同様に同じ方向へスライドしますが、processLine()の合体判定においてcurrent.type !== 'glitch' && next.type !== 'glitch' && current.value === next.valueというガードがかかっているため、GLITCH同士をぶつけても合体して消えることはありません。
  3. 描画上の識別:js/game-renderer.jsではtype === 'glitch'のタイルに対し、数値の代わりに#記号を描画し、マゼンタ系のグリッチ発光マテリアルを割り当てています。

放置すればそのまま盤面の空き容量を圧迫するデッドウェイトになるわけですが、ここで救済ギミックとなるのが「6近傍ショックウェーブ破壊」です。

03. 6近傍ショックウェーブ誘爆ロジックと「溜めて割る」掃除戦術

GLITCHブロックを消去する唯一の方法は、「GLITCHブロックの上下左右前後(6近傍)のいずれかの隣接マスで、通常の数字ブロック同士のマージを発生させること」です。js/game-core.jsのdestroyAdjacentGlitches()メソッドに記述されているショックウェーブ判定コードを確認してみましょう。

// js/game-core.js : HARDモードの6近傍ショックウェーブ誘爆処理
destroyAdjacentGlitches(mergeEvents) {
  const dirs = [
    [1, 0, 0], [-1, 0, 0],
    [0, 1, 0], [0, -1, 0],
    [0, 0, 1], [0, 0, -1]
  ];
  const destroyed = [];
  const seen = new Set();

  for (const ev of mergeEvents) {
    const { x, y, z } = ev.to;
    for (const [dx, dy, dz] of dirs) {
      const nx = x + dx, ny = y + dy, nz = z + dz;
      if (nx < 0 || nx >= this.size || ny < 0 || ny >= this.size || nz < 0 || nz >= this.size) continue;
      const neighbor = this.grid[nx][ny][nz];
      if (neighbor && neighbor.type === 'glitch' && !seen.has(neighbor.id)) {
        seen.add(neighbor.id);
        this.grid[nx][ny][nz] = null;
        this.glitchesDestroyed += 1;
        this.score += 50;
        destroyed.push({ id: neighbor.id, x: nx, y: ny, z: nz });
      }
    }
  }
  return destroyed;
}

このコードから読み取れる重要な戦術的仕様が3つあります。

  • 判定タイミングは「全ラインのスライド&マージ着地後」:マージが成立した着地座標ev.to (x, y, z)を起点として、移動完了後にその6近傍(±1, 0, 0), (0, ±1, 0), (0, 0, ±1)にいるGLITCHをすべて一括消去します。
  • 1回のマージで複数のGLITCHを同時爆破可能:for (const [dx, dy, dz] of dirs)で6方向すべてを走査するため、1箇所のマージ座標に3個のGLITCHが隣接していれば、3個とも同時に吹き飛びます。
  • 破壊ボーナス+50点とターン終了時の空きマス確保:この誘爆処理は新規タイルがスポーンする直前に実行されます。つまり、誘爆によってGLITCHが消えたマスは即座に空きマスに戻り、盤面窒息を劇的に緩和してくれるわけです。

注意したいのは、「斜め隣接(マンハッタン距離2以上)では誘爆しない」という点です。必ずX・Y・Zいずれかの軸で面と面が接している必要があります。そのため、GLITCHが湧いたときは慌てて追いかけるのではなく、外周の壁際へスライドで押し固めておき、そのすぐ隣に「2と2」や「4と4」を滑り込ませて一気にまとめて爆破するのが最も手数の無駄がない戦い方になります。

04. チャージ制Multi-UNDOの仕様(最大3スタック・4合体で+1回復)

さて、2個湧きとGLITCHの二重苦だけでは理不尽な運ゲーになりかねないため、本編の戦術リソースとして実装したのが「チャージ制Multi-UNDO」です。以下のパラメータでUNDOチャージと過去履歴スタックが循環します。

『2048×3D』チャージ制Multi-UNDOの内部パラメータ一覧
パラメータ名(game-core.js) 設定値 ゲーム内での意味と挙動
maxUndoCharges 3 保有できるUNDOチャージの最大ストック数
初期undoCharges 1 ゲーム開始時点で最初から使えるUNDO回数
mergesPerCharge 4 UNDOチャージを+1回復するために必要な累計マージ回数
undoStack最大長 3 巻き戻し可能な過去ターンの盤面スナップショット保持数(最大3手前まで連続巻き戻し可能)

実装コードを見ると、1ターン中に2組同時マージが発生した場合は2組分としてカウントされ、4合体ごとにthis.undoCharges = Math.min(this.maxUndoCharges, this.undoCharges + 1)でチャージが1つ回復します。

ここで非常に重要なテクニックがあります。undo()は盤面配列・スコア・コンボ数を1手前のスナップショットに戻すため、「うっかりコンボが途切れて2個湧き+GLITCHを引いてしまった悪手」をなかったことにできるのです。さらに、undoChargesが3個満タンの状態で4合体を達成しても4個目にはならず切り捨てられてしまうため、チャージが3に達しているときは積極的に1回使って常に2チャージ前後で回す方がリソース効率が高くなります。

05. HARDモードで盤面窒息を防ぐ実戦チェックリスト

ここまでのコード仕様を踏まえ、HARDモードでハイスコアを狙うための実戦セオリーを3点にまとめます。

  1. アンカー角((0,0,0))の隣にGLITCHを居座らせない:最大タイルのすぐ隣にGLITCHが入り込むと、2番手・3番手タイルとの合体動線が完全に遮断されます。もしアンカー付近にGLITCHが滑り込んだ場合は、即座にその隣で小さな数字(2+2や4+4)をマージさせて爆破するか、UNDOで1手戻して別方向から合体させます。
  2. 「整列だけの空振り手」を打つ前に全6方向を確認する:コンボが途切れると確定2個スポーンになるため、少し形が悪くても「どこかで2+2が合体する方向」を優先した方が盤面の空きマスが多く残るケースが多々あります。
  3. X-Ray / Slicerで中央(1,1,1)のGLITCHを見落とさない:一番怖いのが、3×3×3のど真ん中(1,1,1)にGLITCHが入り込み、外周タイルに囲まれて見えなくなるパターンです。HARDモードではこまめにX-Ray透過表示やLayer Slicerを切り替え、内部にGLITCHが潜んでいないか点検する習慣をつけると生存率が跳ね上がります。

次回(第4回)は、3×3×3のパズル思考から一転して、4×4×4(64マス)の広大な空間でスピードと倍率を競う「PARTYモードの+TIME出現率とFEVERタイマー凍結仕様」について詳しく解説します。それでは、HARDモードのランキングでも対戦お願いします!

HARD Mode Challenge

HARDモードでGLITCH誘爆とMulti-UNDOを試す

コンボを繋いで2個湧きを抑え、6近傍ショックウェーブでGLITCHを一掃しよう。

🔥 HARDモードに挑戦する ➔ 📐 練習場で合体ルールを確認