paizaラーニング問題集「幅のある移動」を解いてみた

https://paiza.jp/works/mondai/a_rank_level_up_problems/a_rank_snake_mapmove_step5

▼考え方

この問題を解くために私が考えた内容1～6を以下に示します。

1. 現在向いている方角を示す変数neswと、移動の向きと移動するマス数を格納したリストdlを、関数caldistanceに渡して、y,x座標の移動距離を計算します。

2. 現在の座標(y,x)と1の戻り値(y,x座標の移動距離)を関数nextpositionに渡して、移動が可能かどうかを判定を行い、移動が不可能ならば、障害物がある旨のメッセージあるいはマップの範囲外である旨のメッセージを返します。さらに移動後の座標を返します。2に関しては、以下に詳細な説明を示します。

3. y軸方向において、スタート位置を含む移動の際に通ったマップのマスを'*'に変更します。

4. x軸方向において、スタート位置を含む移動の際に通ったマップのマスを'*'に変更します。

5. 移動後の座標を現在の座標に設定します。

6. 移動後の向いている方角を求めます。

[考え方2の詳細な説明]

2.1
y軸方向の移動(dyx[1] == 0)か、x軸方向の移動(dyx[0] == 0)かで処理を分けます。

2.2
y軸方向の移動の処理をするために、2次元リストSから列を抽出しリストS_rに格納します。

2.3
y軸の負の方向(dyx[0] < 0)に移動するのか、正の方向に移動するのかで、処理を分けます。

2.4
y軸の負の方向に移動することを考えます。移動(y+dyx[0]からy、あるいは、0からy)の間に障害物がある場合は、障害物がある旨のメッセージと移動後の座標を返します。

2.5
関数calhashindexを用いて障害物の位置を計算し、変数hash_indexに格納します。

2.6
移動の間に障害物がないがマップの範囲外に移動してしまう場合は、マップの範囲外である旨のメッセージと移動後の座標を返します。

2.7
y軸の正の方向に移動することを考えます。移動(yからy+dyx[0]+1、あるいは、yからH-1)の間に障害物がある場合は、障害物がある旨のメッセージと移動後の座標を返します。

2.8
移動の間に障害物がなく、移動がマップの範囲内であった場合は、その旨のメッセージと移動後の座標を返します。

▼コード
```py
H,W,sy,sx,N = map(int,input().split())

S = [list(input()) for _ in range(H)]

# dl: 移動の向きと移動するマス数を格納したリスト
dl = [list(input().split()) for _ in range(N)]

# nesw: 現在向いている方角を示す変数 0:北,1:東,2:南,3:西
nesw = 0

# y: 現在のy座標を示す変数
y = sy

# x: 現在のx座標を示す変数
x = sx

# caldistance: y,x座標の移動距離を計算を計算する関数
def caldistance(nesw,dl_i):

    # dy: y座標の移動距離を示す変数
    dy = 0

    # dx: x座標の移動距離を示す変数
    dx = 0
    
    dl_i[1] = int(dl_i[1])

    if dl_i[0] == "L":
    
        if nesw == 0:
            dx -= dl_i[1]
        elif nesw == 1:
            dy -= dl_i[1]
        elif nesw == 2:
            dx += dl_i[1]
        else:
            dy += dl_i[1]

    else:
        if nesw == 0:
            dx += dl_i[1]
        elif nesw == 1:
            dy += dl_i[1]
        elif nesw == 2:
            dx -= dl_i[1]
        else:
            dy -= dl_i[1]
    
    return [dy,dx]

# calhashindex: 障害物の位置を計算する関数
def calhashindex(S_r_sub):
    
    return [i for i in range(len(S_r_sub)-1,-1,-1) if S_r_sub[i] == '#']

# nextposition: 移動が可能かどうかの判定、移動後の座標を返す関数
def nextposition(y,x,dyx):

    # 考え方2.1.
    if dyx[1] == 0:

        # 考え方2.2.
        # S_r: 2次元リストSから列を抽出し格納するリスト
        S_r = [r[x] for r in S]

        # 考え方2.3.
        if dyx[0] < 0:

            # 考え方2.4.
            # S_r_sub: 移動予定の部分を抽出し格納するリスト
            S_r_sub = S_r[max(y+dyx[0],0):y]
            if "#" in S_r_sub:
                # 考え方2.5.
                # hash_index: 障害物の位置を格納する変数
                hash_index = calhashindex(S_r_sub)
                return ["Stop_#_y",y-(len(S_r_sub)-(hash_index[0]+1)),x]
                
            else:
                # 考え方2.6.
                if y+dyx[0] <= -1:
                    return ["Stop_om",max(0,y+dyx[0]),x]
                    
        else:

            # 考え方2.7.
            S_r_sub = S_r[y:min(y+dyx[0]+1,H-1)]
            if "#" in S_r_sub:
                hash_index = calhashindex(S_r_sub)
                return ["Stop_#_y",y+hash_index[0]-1,x]
            
            else:
                if y+dyx[0] >= H:
                    return ["Stop_om",min(y+dyx[0],H-1),x]

        # 考え方2.8.
        return ["OK",y+dyx[0],x]
            
    else:
    
        if dyx[1] < 0:
            
            S_r_sub = S[y][max(x+dyx[1],0):x]
            if "#" in S_r_sub:
                hash_index = calhashindex(S_r_sub)
                return ["Stop_#_x",y,x-(len(S_r_sub)-(hash_index[0]+1))]

            else:
                if x+dyx[1] <= -1:
                    return ["Stop_om",y,max(0,x+dyx[1])]
        else:
            
            S_r_sub = S[y][x:min(x+dyx[1]+1,W-1)]
            if "#" in S_r_sub:
                hash_index = calhashindex(S_r_sub)
                return ["Stop_#_x",y,x+hash_index[0]-1]

            else:
                if x+dyx[1] >= W:
                    return ["Stop_om",y,min(x+dyx[1],W-1)]
    
        return ["OK",y,x+dyx[1]]

for i in range(N):

    # 考え方1.
    # dyx: y,x座標の移動距離に関する情報を格納するリスト
    dyx = caldistance(nesw,dl[i])

    # 考え方2.
    # checkresult: 移動が可能かどうかの判定結果の文字列を格納するリスト
    # ny: 移動後のy座標を示す変数
    # nx: 移動後のx座標を示す変数
    checkresult,ny,nx = nextposition(y,x,dyx)

    # 考え方3.
    if y < ny:
        y1 = y
        y2 = ny+1
    elif y > ny:
        y1 = ny
        y2 = y+1
    else:
        y1 = 0
        y2 = 0  
    for j in range(y1,y2):
        S[j][x] = "*"

    # 考え方4.
    if x < nx:
        x1 = x
        x2 = nx+1
    elif x > nx:
        x1 = nx
        x2 = x+1
    else:
        x1 = 0
        x2 = 0
    for j in range(x1,x2):
        S[y][j] = "*"

    # 考え方5.
    if checkresult != "OK":
        break
    else:
        y = ny
        x = nx

    # 考え方6.
    if dl[i][0] == "L":
        nesw -= 1
    else:
        nesw += 1
    
    if nesw > 3:
        nesw = 0
    elif nesw < 0:
        nesw = 3

for j in range(H):
    print(*S[j],sep='')
```