Pythonで2048を作ってみた

# はじめに

Qiitaの初めての投稿に加え、独学でプログラムの勉強をしているので、コードや説明内容に下手な部分があるかと思いますが、ご容赦いただけると幸いです。

さて、今回はPythonを使って、『2048』というゲームを作成したので、紹介していきたいと思います。

# 2048とは？

2048というのは、数字パズルのゲームです。
実際に、スマホアプリにもあるゲームで、私自身もよく遊んだ記憶があります。

ルールは、4✖️4マスのボードがあり、スライドをして同じ数字のタイルを重ねてより大きな数字のタイルを作っていくゲームになります。

ゲーム自体は、シンプルですが、意外と奥が深くて難しく、地味にはまってしまうゲームだと思います。

![スクリーンショット 2021-06-24 3.05.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/532772/c5ff111c-16ea-1f66-41cd-795c6084834d.png)

# ソースコード

とりあえず、作成したソースコードを下記に記載します。

```
import pygame
import sys
import math
import random

# ******************** 変数／定数 ********************
# =============== COLOR ===============
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
FLORALWHITE = (255,250,240)
LIGHTGRAY = (211, 211, 211)
GRAY = (128, 128, 128)
RED = (255, 0, 0)
GREEN = (0, 128, 0)
ORANGE = (255, 165, 0)
BLUE = (0, 0, 255)
rc = [204, 238, 238, 243, 243, 248, 249, 239, 239, 239, 239, 239, 95]
gc = [192, 228, 224, 177, 177, 149, 94, 207, 207, 203, 199, 195, 218]
bc = [179, 218, 198, 116, 116, 90, 50, 108, 99, 82, 57, 41, 147]
# =============== SIZE ===============
SCREEN_WIDTH = 700
SCREEN_HEIGHT = 1000
MASU_BLANK_WIDTH = 30
MASU_BLANK_HEIGHT = 330
# =============== DIRECTION ===============
DIR_UP = 0
DIR_DOWN = 1
DIR_LEFT = 2
DIR_RIGHT = 3
COMMAND_NG = 4
# =============== GAME MANAGE===============
idx = 0
tmr = 0
msg = ""
score = 0
high_score = 0
# =============== MASU ===============
MASU_NUM = 4
MASU_SIZE = 160
MASU_HALF_SIZE = int(MASU_SIZE/2)
# =============== BOARD ===============
board = []
back = []
for y in range(MASU_NUM):
    board.append([0]*MASU_NUM)
    back.append([0]*MASU_NUM)

    
# ============================================================
#                           DRAW
# ============================================================

# ******************** 文字の描画 ********************
def draw_text(sc, txt, x, y, siz, col):
    fnt = pygame.font.Font(None, siz)
    sur = fnt.render(txt, True, col)
    # 中央揃え
    x = x - sur.get_width()/2
    y = y - sur.get_height()/2
    # 文字表示
    sc.blit(sur, [x, y])


# ******************** ボードの描画 ********************
def draw_board(sc):
    # ボード表示
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            X = x * MASU_SIZE + MASU_BLANK_WIDTH
            Y = y * MASU_SIZE + MASU_BLANK_HEIGHT
            # マス目の値：0
            if board[y][x] == 0:
                pygame.draw.rect(sc, LIGHTGRAY, [X, Y, MASU_SIZE, MASU_SIZE])
            # マス目の値：0以外
            else:
                index = (int(math.log(board[y][x], 2)) - 1) % 13
                color = (rc[index], gc[index], bc[index])
                # 描画
                pygame.draw.rect(sc, color, [X, Y, MASU_SIZE, MASU_SIZE])
                draw_text(sc, str(board[y][x]), X+MASU_HALF_SIZE, Y+MASU_HALF_SIZE, 100, WHITE)
            # 描画：枠線
            pygame.draw.rect(sc, GRAY, [X, Y, MASU_SIZE, MASU_SIZE], 5)

    if tmr%50 < 40:
        draw_text(sc, msg, SCREEN_WIDTH/2, 230, 50, BLACK)

    # 2048
    draw_text(sc, "2", 50, 80, 100, RED)
    draw_text(sc, "0", 90, 80, 100, GREEN)
    draw_text(sc, "4", 130, 80, 100, ORANGE)
    draw_text(sc, "8", 170, 80, 100, BLUE)
    # SCORE
    pygame.draw.rect(sc, GRAY, [240, 30, 200, 100])
    draw_text(sc, "SCORE", 340, 55, 40, LIGHTGRAY)
    draw_text(sc, str(score), 340, 100, 40, WHITE)
    # HIGH SCORE
    pygame.draw.rect(sc, GRAY, [470, 30, 200, 100])
    draw_text(sc, "HIGH SCORE", 570, 55, 40, LIGHTGRAY)
    draw_text(sc, str(high_score), 570, 100, 40, WHITE)
    # RESTART / UNDO
    draw_text(sc, "RESTART", 120, 300, 50, ORANGE)
    draw_text(sc, "UNDO", 610, 300, 50, ORANGE)


# ============================================================
#                           BOARD
# ============================================================

# 確認：空いたマス目(0)があるか
def board_check():
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            if board[y][x] == 0:
                return True
    return False


# ******************** 空いたマス -> 2のランダム配置 ********************
def random_place():
    while True:
        x = random.randint(0, MASU_NUM-1)
        y = random.randint(0, MASU_NUM-1)
        if board[y][x] == 0:
            board[y][x] = 2
            break


# ******************** 上下左右(↑:w ↓:s ←:a →:d)のコマンド入力 ********************
def command_key(key):

    if key[pygame.K_w] == 1:    # 上方向
        return DIR_UP
    elif key[pygame.K_s] == 1:  # 下方向
        return DIR_DOWN
    elif key[pygame.K_a] == 1:  # 左方向
        return DIR_LEFT
    elif key[pygame.K_d] == 1:  # 右方向
        return DIR_RIGHT
    else:                       # NG方向
        return COMMAND_NG


# ******************** コマンドの入力方向にスライドが可能か確認 ********************
def slide_check(dir_key):
    # 確認：1回以上、数字を動かせたか or 数字を合算することができるか(同じ数字が動かす方向に並んでいる)
    command_ok = False
    
    # 上方向 ／ 下方向
    if dir_key == DIR_UP or dir_key == DIR_DOWN:
        for y in range(MASU_NUM - 1):
            for x in range(MASU_NUM):
                    
                # 上方向
                if dir_key == DIR_UP:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][x] == 0 and board[y+1][x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][x] == board[y+1][x] and board[y][x] != 0:
                        command_ok = True
                # 下方向
                elif dir_key == DIR_DOWN:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[(MASU_NUM-1)-y][x] == 0 and board[(MASU_NUM-2)-y][x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[(MASU_NUM-1)-y][x] == board[(MASU_NUM-2)-y][x] and board[(MASU_NUM-1)-y][x] != 0:
                        command_ok = True
        
    # 左方向 ／ 右方向
    if dir_key == DIR_LEFT or dir_key == DIR_RIGHT:
        for x in range(MASU_NUM - 1):
            for y in range(MASU_NUM):

                # 左方向
                if dir_key == DIR_LEFT:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][x] == 0 and board[y][x+1] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][x] == board[y][x+1] and board[y][x] != 0:
                        command_ok = True
                # 右方向
                if dir_key == DIR_RIGHT:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][(MASU_NUM-1)-x] == 0 and board[y][(MASU_NUM-2)-x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][(MASU_NUM-1)-x] == board[y][(MASU_NUM-2)-x] and board[y][(MASU_NUM-1)-x] != 0:
                        command_ok = True

    return command_ok 


# ******************** 数字をコマンド方向にスライドさせる(スライドが可能かの確認を含む) ********************
def slide(dir_key):
    
    while True:
        # 移動させた回数
        move_count = 0

        # 上方向 ／ 下方向
        if dir_key == DIR_UP or dir_key == DIR_DOWN:
            for y in range(MASU_NUM - 1):
                for x in range(MASU_NUM):
                    
                    # 上方向
                    if dir_key == DIR_UP:
                        # 数字が動かせる場合(数字を動かす先が0)
                        if board[y][x] == 0 and board[y+1][x] != 0:
                            board[y][x] = board[y+1][x]
                            board[y+1][x] = 0
                            move_count += 1
                    # 下方向
                    elif dir_key == DIR_DOWN:
                        # 数字が動かせる場合(数字を動かす先が0)
                        if board[(MASU_NUM-1)-y][x] == 0 and board[(MASU_NUM-2)-y][x] != 0:
                            board[(MASU_NUM-1)-y][x] = board[(MASU_NUM-2)-y][x]
                            board[(MASU_NUM-2)-y][x] = 0
                            move_count += 1
        
        # 左方向 ／ 右方向
        if dir_key == DIR_LEFT or dir_key == DIR_RIGHT:
            for x in range(MASU_NUM - 1):
                for y in range(MASU_NUM):

                    # 左方向
                    if dir_key == DIR_LEFT:
                        # 数字が動かせる場合(数字を動かす先が0)
                        if board[y][x] == 0 and board[y][x+1] != 0:
                            board[y][x] = board[y][x+1]
                            board[y][x+1] = 0
                            move_count += 1
                    # 右方向
                    if dir_key == DIR_RIGHT:
                        # 数字が動かせる場合(数字を動かす先が0)
                        if board[y][(MASU_NUM-1)-x] == 0 and board[y][(MASU_NUM-2)-x] != 0:
                            board[y][(MASU_NUM-1)-x] = board[y][(MASU_NUM-2)-x]
                            board[y][(MASU_NUM-2)-x] = 0
                            move_count += 1
                            
        # 一度も動かさなくなったら、ループを抜ける        
        if move_count == 0:
            break



# ******************** スライド方向に同じ数字が並ぶ場合は数字を合算する ********************
def same_num_check(dir_key):
    global score, high_score
    
    # 上方向 ／ 下方向
    if dir_key == DIR_UP or dir_key == DIR_DOWN:
        for y in range(MASU_NUM - 1):
            for x in range(MASU_NUM):

                # 上方向
                if dir_key == DIR_UP:
                    if board[y][x] == board[y+1][x] and board[y][x] != 0:
                        board[y][x] *= 2
                        board[y+1][x] = 0
                        score += board[y][x]
                # 下方向
                if dir_key == DIR_DOWN:
                    if board[(MASU_NUM-1)-y][x] == board[(MASU_NUM-2)-y][x] and board[(MASU_NUM-1)-y][x] != 0:
                        board[(MASU_NUM-1)-y][x] *= 2
                        board[(MASU_NUM-2)-y][x] = 0
                        score += board[(MASU_NUM-1)-y][x]

    # 左方向 ／ 右方向
    if dir_key == DIR_LEFT or dir_key == DIR_RIGHT:
        for x in range(MASU_NUM-1):
            for y in range(MASU_NUM):

                # 左方向
                if dir_key == DIR_LEFT:
                    if board[y][x] == board[y][x+1] and board[y][x] != 0:
                        board[y][x] *= 2
                        board[y][x+1] = 0
                        score += board[y][x]
                # 右方向
                if dir_key == DIR_RIGHT:
                    if board[y][(MASU_NUM-1)-x] == board[y][(MASU_NUM-2)-x] and board[y][(MASU_NUM-1)-x] != 0:
                        board[y][(MASU_NUM-1)-x] *= 2
                        board[y][(MASU_NUM-2)-x] = 0
                        score += board[y][(MASU_NUM-1)-x]


# ============================================================
#                           GAME
# ============================================================

# ******************** ゲームのリスタート ********************
def game_restart():
    global score

    # スコアの初期化
    score = 0
    # ボードの初期化
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            board[y][x] = 0
            back[y][x] = 0
    

# ******************** 1つ前のボードに戻るためのボードのセーブ ********************
def save():
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            back[y][x] = board[y][x]


# ******************** 1つ前のボードに戻すためにボードをロード ********************
def load():
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            board[y][x] = back[y][x]


# ******************** リスタート／戻る ********************
def restart_undo(mx, my):
    global idx, tmr
    
    # RESTART
    if (30 <= mx <= 210) and (280 <= my <= 320):
        game_restart()
        idx = 1
        tmr = 0
    # UNDO
    elif (550 <= mx <= 670) and (280 <= my <= 320):
        load()


# ******************** ゲームの終了判定 ********************
def game_set():
    # ランダムに数字を置けなくなった(空きのマスがない)場合は、ゲーム終了
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            if board[y][x] == 0:
                return False
    return True


# ============================================================
#                           MAIN
# ============================================================
    
# ******************** メインループ ********************
def main():
    global idx, tmr, msg, high_score
    
    pygame.init()
    pygame.display.set_caption("2048")

    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    clock = pygame.time.Clock()

    tmr = 0

    while True:
        tmr = tmr + 1

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # スクリーン
        screen.fill(FLORALWHITE)
        # キー入力
        key = pygame.key.get_pressed()
        # マウスのクリック
        mBtn_1, mBtn_2, mBtn_3 = pygame.mouse.get_pressed()
        click = mBtn_1
        # マウスのx,y座標
        mouseX, mouseY = pygame.mouse.get_pos()

        # タイトル
        if idx == 0:
            msg = "push [SPACE] to start game!"
            if key[pygame.K_SPACE] == 1:
                idx = 1
                tmr = 0

        # プレイヤープレイ中
        elif idx == 1:
            msg = "W - UP  S - DOWN  A - LEFT  D - RIGHT"
            
            # 最初に、ランダムに「2」を配置
            if tmr == 1:
                random_place()

            elif tmr > 10:
                # 判定：スライドが可能か
                slide_ok = 0
                for di in range(4):
                    if slide_check(di) == True:
                        slide_ok += 1

                # スライド不可 -> ゲーム終了
                if slide_ok == 0:
                    idx = 2
                    tmr = 0

                # スライド可能 -> コマンド入力を受け付ける
                else:
                    dir_key = command_key(key)
                    if dir_key != COMMAND_NG:
                        # 確認：コマンド入力の方向にスライドが可能か
                        if slide_check(dir_key) == True:
                            save()
                            slide(dir_key)              # スライド処理
                            same_num_check(dir_key)     # 同じ数字 -> 数字の合算処理
                            slide(dir_key)              # スライド処理
                            tmr = 0

        # ゲーム終了
        elif idx == 2:
            msg = "GAME OVER!"
            if tmr == 120:
                game_restart()
                idx = 0
                tmr = 0

        # RESTART or UNDO
        if click == True:
            restart_undo(mouseX, mouseY)

        # ハイスコアの更新判定
        if high_score < score:
            high_score = score

        # ボードの描画
        draw_board(screen)

        pygame.display.update()
        clock.tick(30)

if __name__ == '__main__':
    main()
```

# 全体の流れ

ゲーム全体を処理する流れ（メインループ）のソースコードが以下になります。
`global ...`の行から`mouseX, mouseY ...`のスクリーン表示やマウス座標、クリックを取得するためのコードになります。
そして、`タイトル`と書かれたコメント以降の`if idx == 0:`からゲーム全体を処理する流れを書いています。

ゲーム全体の処理は、`idx==0`でタイトル画面、`idx==1`でプレイ中画面、`idx==2`でゲーム終了し、またタイトル画面へ戻るようになっています。

```python
def main():
    global idx, tmr, msg, high_score
    
    pygame.init()
    pygame.display.set_caption("2048")

    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    clock = pygame.time.Clock()

    tmr = 0

    while True:
        tmr = tmr + 1

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()

        # スクリーン
        screen.fill(FLORALWHITE)
        # キー入力
        key = pygame.key.get_pressed()
        # マウスのクリック
        mBtn_1, mBtn_2, mBtn_3 = pygame.mouse.get_pressed()
        click = mBtn_1
        # マウスのx,y座標
        mouseX, mouseY = pygame.mouse.get_pos()

        # タイトル
        if idx == 0:
            msg = "push [SPACE] to start game!"
            if key[pygame.K_SPACE] == 1:
                idx = 1
                tmr = 0

        # プレイヤープレイ中
        elif idx == 1:
            msg = "W - UP  S - DOWN  A - LEFT  D - RIGHT"
            
            # 最初に、ランダムに「2」を配置
            if tmr == 1:
                random_place()

            elif tmr > 10:
                # 判定：スライドが可能か
                slide_ok = 0
                for di in range(4):
                    if slide_check(di) == True:
                        slide_ok += 1

                # スライド不可 -> ゲーム終了
                if slide_ok == 0:
                    idx = 2
                    tmr = 0

                # スライド可能 -> コマンド入力を受け付ける
                else:
                    dir_key = command_key(key)
                    if dir_key != COMMAND_NG:
                        # 確認：コマンド入力の方向にスライドが可能か
                        if slide_check(dir_key) == True:
                            save()
                            slide(dir_key)              # スライド処理
                            same_num_check(dir_key)     # 同じ数字 -> 数字の合算処理
                            slide(dir_key)              # スライド処理
                            tmr = 0

        # ゲーム終了
        elif idx == 2:
            msg = "GAME OVER!"
            if tmr == 120:
                game_restart()
                idx = 0
                tmr = 0

        # RESTART or UNDO
        if click == True:
            restart_undo(mouseX, mouseY)

        # ハイスコアの更新判定
        if high_score < score:
            high_score = score

        # ボードの描画
        draw_board(screen)
```

#### ・ タイトル画面 (idx = 0)

タイトル画面に限らず、全ての画面は最後の行にある、作成した`draw_board()`の関数によって表示しています。

`msg`では、`draw_boad()`関数で、説明文の表示として使用しています。

そして、`if key[pygame.K_SPACE]==1:`により、スペースキーの入力があると、`idx=1`を代入して、ゲームのプレイ画面へ移るようにしています。

---

#### ・ ゲームプレイ中 (idx = 1)

最初に、作成した`random_place()`関数で、ボードの空いたますの中でランダムに数字の2を配置させます。

次に、上下左右のいずれかの方向に、数字のスライドが可能かを確認します。
スライドの確認は、作成した`slide_check()`で行い、数字の0を上スライド、1を下スライド、2を左スライド、3を右スライドに割り当てることで、for文を使用して順に各方向のスライドが行えるか確認を行なっています。

ここで、スライドが行えなければ、`idx=2`を代入して、ゲーム終了処理へ進みます。

スライドが可能であれば、作成した｀command_key()`により、コマンド入力を受け付けます。
スライド方向　→ 上：Wキー、下：Sキー、左：Aキー、右：Dキーを割り当てています。

適切なコマンド（スライド可能）が入力されれば、スライドする前の数字の配置位置を作成した`save()`関数で保存します。
これは、UNDO（1つ前に戻る）機能で使用します。

そして、最初に作成した`slide()`関数で数字をスライドさせます。
次に、`same_num_check()`関数で、数字の合算ができるかを確認し、可能であれば合算します。
この時、数字の合算処理を行なった場合、1マス空きが生じてしまいます。
そのため、もう一度`slide()`関数を呼び出して、生じた空きのマスを埋めています。

そして、数字のスライドが可能な限り、上記の流れを繰り返します。

---

#### ・ ゲーム終了 (idx = 2)

`msg`でGAME OVERを画面に表示させ、プレイヤーにゲーム終了を示します。

そして、一定の時間が経てば`game_restart()`を呼び出し、ゲーム画面を初期の画面に戻し、`idx=0`を代入して、タイトル画面に移ります。

ゲーム全体の流れは、以上になります。
また、画面上の`RESTART`もしくは`UNDO`をクリックすると、作成した`restart_undo()`を呼び出し、いつでも処理が実行できるようにしています。

# 作成した関数の紹介

以降、作成した関数の一部（主に重要な）を紹介します。

---

#### ・ ゲーム画面の表示

ゲームの画面を表示するための`draw_board()`関数のソースコードを以下に示します。

```python
def draw_board(sc):
    # ボード表示
    for y in range(MASU_NUM):
        for x in range(MASU_NUM):
            X = x * MASU_SIZE + MASU_BLANK_WIDTH
            Y = y * MASU_SIZE + MASU_BLANK_HEIGHT
            # マス目の値：0
            if board[y][x] == 0:
                pygame.draw.rect(sc, LIGHTGRAY, [X, Y, MASU_SIZE, MASU_SIZE])
            # マス目の値：0以外
            else:
                index = (int(math.log(board[y][x], 2)) - 1) % 13
                color = (rc[index], gc[index], bc[index])
                # 描画
                pygame.draw.rect(sc, color, [X, Y, MASU_SIZE, MASU_SIZE])
                draw_text(sc, str(board[y][x]), X+MASU_HALF_SIZE, Y+MASU_HALF_SIZE, 100, WHITE)
            # 描画：枠線
            pygame.draw.rect(sc, GRAY, [X, Y, MASU_SIZE, MASU_SIZE], 5)

    if tmr%50 < 40:
        draw_text(sc, msg, SCREEN_WIDTH/2, 230, 50, BLACK)

    # 2048
    draw_text(sc, "2", 50, 80, 100, RED)
    draw_text(sc, "0", 90, 80, 100, GREEN)
    draw_text(sc, "4", 130, 80, 100, ORANGE)
    draw_text(sc, "8", 170, 80, 100, BLUE)
    # SCORE
    pygame.draw.rect(sc, GRAY, [240, 30, 200, 100])
    draw_text(sc, "SCORE", 340, 55, 40, LIGHTGRAY)
    draw_text(sc, str(score), 340, 100, 40, WHITE)
    # HIGH SCORE
    pygame.draw.rect(sc, GRAY, [470, 30, 200, 100])
    draw_text(sc, "HIGH SCORE", 570, 55, 40, LIGHTGRAY)
    draw_text(sc, str(high_score), 570, 100, 40, WHITE)
    # RESTART / UNDO
    draw_text(sc, "RESTART", 120, 300, 50, ORANGE)
    draw_text(sc, "UNDO", 610, 300, 50, ORANGE)
```

ゲームのマスは、配列`board[y][x]`を使って管理しているため、for文を入れ子で使用しています。
`MASU_SIZE`は定数4です。（ゲームは、4✖️4マスのため）

大文字のX、Yはゲームのマス目のx座標、y座標を表しています。
（小文字のx,yは、配列の位置を表しています）
`MASU_BLANK_WIDTH`と`MASU_BLANK_HEIGHT`で縦横位置の調整をしています。

マスが0の時は、四角の薄いグレーで塗りつぶします。
マスが0以外の場合は、色の塗りつぶしと、数字を表示させます。

最後に、マスの枠線を描きます。

以降は、`msg`に代入されている文字の表示や装飾を配置しています。

---

#### ・ スライド：可能かを確認する処理

次にスライドが可能かを確認する`slide_check()`関数です。
ここでは、キー入力された方向に、数字をスライドさせることができるか、もしくは数字の合算処理が行えるかの確認をしています。

```python
def slide_check(dir_key):
    # 確認：1回以上、数字を動かせたか or 数字を合算することができるか(同じ数字が動かす方向に並んでいる)
    command_ok = False
    
    # 上方向 ／ 下方向
    if dir_key == DIR_UP or dir_key == DIR_DOWN:
        for y in range(MASU_NUM - 1):
            for x in range(MASU_NUM):
                    
                # 上方向
                if dir_key == DIR_UP:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][x] == 0 and board[y+1][x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][x] == board[y+1][x] and board[y][x] != 0:
                        command_ok = True
                # 下方向
                elif dir_key == DIR_DOWN:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[(MASU_NUM-1)-y][x] == 0 and board[(MASU_NUM-2)-y][x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[(MASU_NUM-1)-y][x] == board[(MASU_NUM-2)-y][x] and board[(MASU_NUM-1)-y][x] != 0:
                        command_ok = True
        
    # 左方向 ／ 右方向
    if dir_key == DIR_LEFT or dir_key == DIR_RIGHT:
        for x in range(MASU_NUM - 1):
            for y in range(MASU_NUM):

                # 左方向
                if dir_key == DIR_LEFT:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][x] == 0 and board[y][x+1] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][x] == board[y][x+1] and board[y][x] != 0:
                        command_ok = True
                # 右方向
                if dir_key == DIR_RIGHT:
                    # 数字が動かせる場合(数字を動かす先が0)
                    if board[y][(MASU_NUM-1)-x] == 0 and board[y][(MASU_NUM-2)-x] != 0:
                        command_ok = True
                    # 0以外で数字が並ぶ場合 -> 動かせないが[same_num_check()]で合算できるため、コマンドOK
                    elif board[y][(MASU_NUM-1)-x] == board[y][(MASU_NUM-2)-x] and board[y][(MASU_NUM-1)-x] != 0:
                        command_ok = True

    return command_ok 
```

最初に、キー入力で得られたスライド方向の上と下方向の確認を行います。
for文の入れ子を使って、上方向の場合は上から順番に、下方向の場合はしたから順番に確認を行なっていきいます。

キー入力が上方向だった場合(`if dir_key == DIR_UP:`)を見てみます。
`if board[y][x] == 0 and board[y+1][x] != 0:`の文によって、上下隣の上のマス目が0、下のマス目が0以外の数字だった場合、数字をスライドすることができるので、`command_ok = True`を代入します。

また、`elif board[y][x] == board[y+1][x] and board[y][x] != 0:`の文によって、上下隣のマス目が0以外の同じ数字だった場合は、数字の合算ができるため`command_ok = True`を代入しています。

他の方向のキー入力の場合でも、上記2つの条件を確認しています。

最後に、`return command_ok`を返すことによって、コマンド方向に数字のスライドが可能かを確認しています。
（`command_ok == True`でコマンド方向のスライドが可能であることを示す）

---

#### ・ スライド／数字の合算処理

実際に行う、スライドと数字の合算処理は、上記のスライド確認処理とほとんど一緒になります。
それぞれの条件で、`True`となる時に数字を移動させたり、数字の合算を行なっています。

また、"スライド確認処理"と実際の"スライド処理"や"数字の合算処理"を分けて書くことで、コードの記述量は増えますが、分かりやすいかな〜と思ったので、分けて書くことにしました。

# 所感

今回の2048ゲームの制作をして、ゲーム自体はシンプルで作りやすいけど、地味につまづいたところもあるなーと感じました。
主に、配列を使った処理になるので、配列処理に関しての理解が深まったかなーと感じています。

制作した2048の動画を下記に載せてありますので、ご参考に慣れば幸いです。
https://note.com/nakatsu746/n/nab528e168592

かなり、殴り書きみたいな感じで読みにくかったと思いますが、最後まで見ていただいた方、本当にありがとうございました。