paizaラーニング問題集「へび」を解いてみた

▼問題

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

▼考え方

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

1. へびの移動は最大100回なので、100回ループを回します。

2. 以下の3つの引数を関数caldistanceに渡して、y,x方向の移動距離を計算します。移動距離の情報を返します。関数に渡す引数は、①現在の時刻を示す変数i、②現在向いている方角を示す変数nesw、③方向転換する時刻と転換する向きを格納したリストtd 1行ずつ、です。

3. 現在の座標(y,x)と2の戻り値(y,x方向の移動距離)を関数nextpositionに渡して、移動が可能かどうかの判定を行います。移動が不可能ならば"stop"を返します。移動が可能ならば"ok"を返します。さらに、次の座標を返します。考え方3の詳細は、以下に示します。

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

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

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

7. 方向転換した場合は向いている方角を更新します。

[考え方3の詳細]
3.1
y軸方向の移動(dyx[1] == 0)か、x軸方向の移動(dyx[0] == 0)かで処理を分けます。
3.2
y軸方向の移動の処理をするために、2次元リストSから列を抽出しリストS_rに格納します。
3.3
y軸の負の方向(dyx[0] < 0)に移動するのか、正の方向に移動するのかで、処理を分けます。
3.4
y軸の負の方向に移動することを考えます。移動(y+dyx[0]からy、あるいは、0からy)に際して、障害物("#")があるか、自分の身体("*")があるか、マップ外かどうかを判定します。（移動に際し、障害物や自分の身体がある場合は）関数calforwordindexを用いてその位置を計算します。

▼コード
```py
############### 関数処理 ###############

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

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

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

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

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

# calforwordindex: 障害物や自分の身体の位置を計算する関数
def calforwordindex(S_r_sub,mark):
    
    return [l for l in range(len(S_r_sub)-1,-1,-1) if S_r_sub[l] == mark]

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

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

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

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

            # 考え方3.4.
            # S_r_sub: 移動予定の部分を抽出し格納するリスト
            S_r_sub = S_r[max(y+dyx[0],0):y]

            if "#" in S_r_sub:
                index = calforwordindex(S_r_sub,"#")
                return ["stop",y-(len(S_r_sub)-(index[0]+1)),x]
                
            if "*" in S_r_sub:
                index = calforwordindex(S_r_sub,"*")
                return ["stop",y-(len(S_r_sub)-(index[0]+1)),x]

            if ("#" not in S_r_sub) and ("*" not in S_r_sub) and (y+dyx[0] <= -1):
                return ["stop",max(0,y+dyx[0]),x]

        else:
            
            S_r_sub = S_r[y+1:min(y+dyx[0]+1,H-1)]
            
            if "#" in S_r_sub:
                index = calforwordindex(S_r_sub,"#")
                return ["stop",y+index[0]-1,x]
        
            if "*" in S_r_sub:
                index = calforwordindex(S_r_sub,"*")
                return ["stop",y+index[0]-1,x]
        
            if ("#" not in S_r_sub) and ("*" not in S_r_sub) and (y+dyx[0] >= H):
                return ["stop",min(y+dyx[0],H-1),x]
        
        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:
                index = calforwordindex(S_r_sub,"#")
                return ["stop",y,x-(len(S_r_sub)-(index[0]+1))]
                
            if "*" in S_r_sub:
                index = calforwordindex(S_r_sub,"*")
                return ["stop",y,x-(len(S_r_sub)-(index[0]+1))]
            
            if ("#" not in S_r_sub) and ("*" not in S_r_sub) and (x+dyx[1] <= -1):
                return ["stop",y,max(0,x+dyx[1])]
        
        else:
            
            S_r_sub = S[y][x+1:min(x+dyx[1]+1,W-1)]
            
            if "#" in S_r_sub:
                index = calforwordindex(S_r_sub,"#")
                return ["stop",y,x+index[0]-1]

            if "*" in S_r_sub:
                index = calforwordindex(S_r_sub,"*")
                return ["stop",y,x+index[0]-1]

            if ("#" not in S_r_sub) and ("*" not in S_r_sub) and (x+dyx[1] >= W):
                return ["stop",y,min(x+dyx[1],W-1)]
    
        return ["ok",y,x+dyx[1]]

############### メイン関数処理 ###############

H,W,sy,sx,N = map(int,input().split())

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

# td: 方向転換する時刻と転換する向きを格納したリスト
td = [list(input().split()) for _ in range(N)]

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

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

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

# j: リストtdを1行ずつ操作するための変数
j = 0

# 考え方1.
for i in range(100):

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

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

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

    # 考え方5.
    if x < nx:
        x1 = x
        x2 = nx+1
    elif x > nx:
        x1 = nx
        x2 = x+1
    else:
        x1 = 0
        x2 = 0
    for k in range(x1,x2):
        S[y][k] = "*"
    
    if checkresult != "ok":
        break
    else:
        # 考え方6.
        y = ny
        x = nx

        # 考え方7.
        if i == int(td[j][0]):

            if td[j][1] == "L":
                nesw -= 1
            else:
                nesw += 1
    
            if nesw > 3:
                nesw = 0
            elif nesw < 0:
                nesw = 3
            
            if j < len(td)-1:
                j += 1

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