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