Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで2048ゲームの指定方向スライド後の盤面を求める方法


問題の概要

2048ゲームの盤面(初期状態)と、スワイプ方向を表す文字列が与えられたとき、その方向に1回スライドした後の盤面の状態を求めるプログラムを作成します。

2048ゲームでは、4×4のマスに数字が配置されたボードを使用します(空きマスは0で表現されます)。プレイヤーは「U(上)」「D(下)」「L(左)」「R(右)」の4方向のいずれかへスワイプできます。スワイプを行うと、すべての数字はその方向へできるだけ遠くまで移動し、隣り合う同じ数値はちょうど1回だけ合算されます。

たとえば、次のような盤面が与えられたとします。

matrix = [
[2, 0, 0, 2],
[2, 2, 2, 2],
[0, 4, 2, 2],
[2, 2, 2, 0]]

direction = "L"(左スワイプ)の場合、出力は次のようになります。

[
[4, 0, 0, 0],
[4, 4, 0, 0],
[4, 4, 0, 0],
[4, 2, 0, 0]]

解法の考え方

この問題は、以下の手順で解くことができます。

  • directionが「R」の場合
    → boardを反時計回りに2回回転する
  • directionが「U」の場合
    → boardを反時計回りに1回回転する
  • directionが「D」の場合
    → boardを反時計回りに3回回転する
  • i を 0〜3 の範囲で繰り返し処理する
    • row := board[i] 内の非ゼロ要素のみを抽出したリストを作る
    • j を 0〜2 の範囲で繰り返し処理する
      • j + 1 < len(row) かつ row[j] == row[j + 1] の場合
        • row[j] := row[j] * 2(合算する)
        • row[j + 1] を削除する
    • len(row) < 4 の間、row の末尾に 0 を挿入する
    • board[i] := row とする
  • directionが「R」の場合
    → boardを反時計回りに2回回転して元の向きへ戻す
  • directionが「U」の場合
    → boardを反時計回りに3回回転して元の向きへ戻す
  • directionが「D」の場合
    → boardを反時計回りに1回回転して元の向きへ戻す
  • board を返す

このアプローチのポイントは、「左へのスライド」処理だけを実装すればよいという点です。他の方向については、あらかじめボードを回転させて左スライドの向きに揃えてから処理し、最後に元の向きへ戻すことで統一的に扱えます。

実装例

理解を深めるために、実際の実装を見てみましょう。

class Solution:
    def solve(self, board, direction):
        if direction == "R":
            board = rot_anti_clock_dir(rot_anti_clock_dir(board))
        elif direction == "U":
            board = rot_anti_clock_dir(board)
        elif direction == "D":
            board = rot_anti_clock_dir(rot_anti_clock_dir(rot_anti_clock_dir(board)))

        for i in range(4):
            row = [x for x in board[i] if x]
            for j in range(3):
                if j + 1 < len(row) and row[j] == row[j + 1]:
                    row[j] *= 2
                    del row[j + 1]
                while len(row) < 4:
                    row += [0]
            board[i] = row

        if direction == "R":
            board = rot_anti_clock_dir(rot_anti_clock_dir(board))
        elif direction == "U":
            board = rot_anti_clock_dir(rot_anti_clock_dir(rot_anti_clock_dir(board)))
        elif direction == "D":
            board = rot_anti_clock_dir(board)
        return board


def rot_anti_clock_dir(x):
    x = [[x[i][j] for i in range(4)] for j in range(4)]
    return x[::-1]

ob = Solution()
matrix = [
[2, 0, 0, 2],
[2, 2, 2, 2],
[0, 4, 2, 2],
[2, 2, 2, 0]]
print(ob.solve(matrix, "L"))

入力

matrix = [
[2, 0, 0, 2],
[2, 2, 2, 2],
[0, 4, 2, 2],
[2, 2, 2, 0]]

出力

[
[4, 0, 0, 0],
[4, 4, 0, 0],
[4, 4, 0, 0],
[4, 2, 0, 0]]

補足:回転関数の仕組み

ヘルパー関数 rot_anti_clock_dir() は、行列を転置したうえで行の順序を逆転させることで、反時計回り90度の回転を実現しています。このテクニックにより、上下左右どの方向のスワイプも「左スライド」のロジック1つに集約できるのが、この実装の大きな特徴です。各処理の計算量はボードサイズが固定(4×4)であるため、常に一定時間で完了します。

  1. Pythonでボールが入る箱の行と列の位置を効率的に求める方法

    2つの配列AとBがあるとします。配列Aのサイズは行数を表し、A[i]はi行目に存在する箱の個数を意味します。一方、配列Bはボールの配列であり、B[i]はボールに書かれた番号を表します。各ボールi(値がB[i])は、先頭から数えてB[i]番目の箱に配置されるものとします。このとき、B[i]のそれぞれに対応する箱の「行」と「列」を求めるのが本記事の目的です。 例えば、入力が A = [3, 4, 5, 6]、B = [1, 3, 5, 2] の場合、出力は [(1, 1), (1, 3), (2, 2), (1, 2)] となります。具体的な対応関係は以下の通りです。 B[0] = 1 →

  2. Pythonで行列の転置を求める方法をわかりやすく解説

    本記事では、Pythonを使って行列の転置(transpose)を求める方法について、2つのアプローチを交えながら詳しく解説します。 問題の概要 問題設定: 与えられた行列に対して、その転置行列を求めて表示します。 行列の転置とは、行列Aの要素 A[i][j] を A[j][i] と入れ替えることで得られる行列のことです。つまり、行と列を入れ替えた新しい行列が転置行列となります。 それでは、実際のコード実装を見ながら概念を確認していきましょう。 方法1:新しい行列を作成して転置を格納する 1つ目の方法は、入力行列とは別に新しい行列を用意し、そこに転置結果を格納するアプローチです。元の行列を保持