第127回【Python】ロボットの暴走

現在取り組んでいるのは、paiza ラーニング問題集「クラス・構造体メニュー」になります。

はじめに

猫とキャンプと野球観戦と AWS が大好きな旦那、LeoSaki です。モフモフしたい。

Python をゼロから勉強してみよう、のコーナー 127 回目です。

「クラス・構造体メニュー」の最後。とても勉強になって面白かったので、もっと続けたい気持ちがあります。今更ながら、Python と VBA の比較は、深く内容を追求するのにあっていたんじゃないかと思っています。VBA でここまで出来るとは思っていなかった。

それでは、今日も頑張ってみようと思います。

ロボットの暴走

paiza 株式会社では、物品の管理のために上の図のような座標系の広さが無限大のマスの工場 で 番号 1 〜 N が割り当てられた N 台のロボットを運用していました。ところがある日、全てのロボットが暴走してしまいました。各ロボットは性能ごとにレベル分けされており、次の通り移動距離が決まっています。

Lv1 : 特定の方角に 1 マス進む
Lv2 : 特定の方角に 2 マス進む
Lv3 : 特定の方角に 5 マス進む
Lv4 : 特定の方角に 10 マス進む

また、工場のマスのうち 10 マスには工具箱が置かれており、移動後にそのマスにロボットがぴったり止まっていた場合、そのロボットのレベルが 1 上がってしまいます(最大レベルは 4)。
レベル l のロボットの初期位置が工具箱の置かれているマスであったとしても、そのロボットのレベルは l で始まることに気をつけてください。

幸い、初めにロボットがいる範囲や工具箱の置かれているマス、各ロボットの位置とレベル、また、どのロボットがどのような順番でどの方角に移動するかの情報はわかっているので、ロボットの移動が K 回終わったときの各ロボットの位置とレベルを推定してください。

H W N K
lx_1 ly_1
...
lx_10 ly_10
x_1 y_1 l_1
...
x_N y_N l_N
r_1 d_1
...
r_K d_K

・ 1 行目では ロボットの初期位置の y , x 座標の上限についての整数 H , W , ロボットの数 N , ロボットの移動回数 K が半角スペース区切りで与えられます。
・ 続く 10 行のうち i 行目では、i 個目の工具箱が置かれたマスの x , y 座標 x_i , y_i が与えられます。(1 ≦ i ≦ 10)
・ 続く N 行のうち i 行目では、 番号 i のロボットの初期位置の x 座標 x_i , y 座標 y_i , レベル l_i が半角スペース区切りで与えられます。
・ 続く K 行のうち i 行目では、 i 回目の移動を行ったロボットの番号 r_i と移動の方角 d_i が与えられます。


すべてのテストケースにおいて、以下の条件をみたします。

・ 5 ≦ H , W , N , K ≦ 10^5
・ 0 ≦ lx_i < W , 0 ≦ ly_i < H (1 ≦ i ≦ 10)
・ 0 ≦ x_i < W , 0 ≦ y_i < H , 1 ≦ l_i ≦ 4 (1 ≦ i ≦ N)
・ 1 ≦ r_i ≦ N
・ d_i は “N" , “S" , “E" , “W" のいずれか (1 ≦ i ≦ K) で、それぞれ 北・南・東・西 へ移動したことを表す。


入力例

5 5 3 3
0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 1 1
2 2 1
2 3 1
1 W
1 E
3 S

出力例

3 1 2
2 2 1
2 4 1

物品の管理って何の管理をしている工場なのかが気になる。危険物を取り扱っている工場だったらと考えると怖い。と、問題とはまったく関係ないことを考えてしまった。

Python
class Robot:
    width = {1:1, 2:2, 3:5, 4:10}
    dist = {'N':[0,-1], 'E':[1,0], 'S':[0,1], 'W':[-1,0]}
    
    def __init__(self,x,y,l):
        self.x = x
        self.y = y
        self.l = l
        self.m = self.width[self.l]
    
    def levelup(self):
        if self.l < 4:
            self.l += 1
            self.m = self.width[self.l]
    
    def move(self,direction):
        self.x += self.dist[direction][0] * self.m
        self.y += self.dist[direction][1] * self.m
    
    def get_possition(self):
        return [self.x,self.y]
    
    def print(self):
        print(self.x,self.y,self.l)
        
H,W,N,K = map(int,input().split())
toolbox = [[None] for _ in range(10)]
robots = [None] * N

for i in range(10):
    x,y = map(int,input().split())
    toolbox[i] = [x,y]

for i in range(N):
    x,y,l = map(int,input().split())
    robots[i] = Robot(x,y,l)

for i in range(K):
    s = input().split()
    idx,direction = int(s[0])-1,s[1]
    robots[idx].move(direction)
    now_p = robots[idx].get_possition()
    if now_p in toolbox:
        robots[idx].levelup()

for robot in robots:
    robot.print()

最初、レベルが幾つだったら動くマス数は幾つ、って書いたのだけれど、レベルや方向が分かれば共通にできる、と気づいてからは早かった。気づくか気づかないかは、実践をどれだけ多く積むか、なのでしょうか。

VBA
## Robot
Private lv As Integer
Private x_cood As Integer
Private y_cood As Integer
Private m As Integer
Private wid As Object
Private dist As Object

Private Sub Class_Initialize()
    
    Set wid = CreateObject("Scripting.Dictionary")
    wid.Add 1, 1
    wid.Add 2, 2
    wid.Add 3, 5
    wid.Add 4, 10
    
    Set dist = CreateObject("Scripting.Dictionary")
    dist.Add "N", Array(0, -1)
    dist.Add "E", Array(1, 0)
    dist.Add "S", Array(0, 1)
    dist.Add "W", Array(-1, 0)
    
End Sub

Property Let init(x, y, l)

    x_cood = x
    y_cood = y
    lv = l
    m = wid(lv)
    
End Property

Function levelup()

    If lv < 4 Then
        lv = lv + 1
        m = wid(lv)
    End If
    
End Function

Function move(direction)

    temp = dist(direction)
    x_cood = x_cood + temp(0) * m
    y_cood = y_cood + temp(1) * m
    
End Function

Property Get possition()

    possition = Array(x_cood, y_cood)
    
End Property

Function result_print()

    Debug.Print x_cood & " " & y_cood & " " & lv
    
End Function
## 標準モジュール
Sub class_primer__robot_move()

    HWNK = Split(Cells(1, 1), " ")
    h = Val(HWNK(0))
    w = Val(HWNK(1))
    N = Val(HWNK(2))
    K = Val(HWNK(3))
    
    Dim toolbox(9, 1) As Integer
    For i = 0 To 9
        xy = Split(Cells(i + 2, 1), " ")
        x = Val(xy(0))
        y = Val(xy(1))
        toolbox(i, 0) = x
        toolbox(i, 1) = y
    Next
    
    Dim robots() As New Robot
    ReDim robots(N - 1)
    
    For i = 0 To N - 1
        xyl = Split(Cells(i + 12, 1), " ")
        x = Val(xyl(0))
        y = Val(xyl(1))
        l = Val(xyl(2))
        robots(i).init(x, y) = l
    Next
    
    For i = 1 To K
        s = Split(Cells(i + N + 11, 1), " ")
        idx = Val(s(0)) - 1
        direction = s(1)
        robots(idx).move (direction)
        temparr = robots(idx).possition
       
         For j = 0 To 9
            If toolbox(j, 0) = temparr(0) And toolbox(j, 1) = temparr(1) Then
                robots(idx).levelup
                Exit For
            End If
        Next
    Next
    
    For i = LBound(robots) To UBound(robots)
        robots(i).result_print
    Next
    
End Sub

クラスの initialize で連想配列を作るよりも、外部で定数を作った方がいいんじゃないかとも思うが、問題の趣旨に沿って、クラス内で作成した。

最後に

1 問に Python と VBA で 2 回取り組んでいると、結構、新しい気付きがある。今回もこれまでも、最初に書いた回答に比べれば、ずっと洗練されたものになっていると思う。それは、Python で解いた後、VBA で取り組んでいる際に、ここをこうすればもっと分かりやすくなるのでは、とか、計算量を減らせるのでは、と思って修正を行っているからだ。

ゼロから勉強する、がテーマだから、多少時間をかけても、より良いものを求めて問題に取り組む姿勢は続けていきたいと思っている。

引き続き、よろしくお願いいたします!

Python学習,Python,paiza

Posted by LeoSaki