LoginSignup
0
0

More than 3 years have passed since last update.

Rubyでheavy(蛇)!?

Last updated at Posted at 2020-10-25

0.恐竜×スキルチェック

ここのところ激務につき、ほとんどスキルアップできていなかった。
念願のテレワークになっているのに何故か、テレワーク前より忙しくなっている。
そして何より、恐竜ちゃんを出すプログラムのアイデアがなくなりつつある。
さらに最近知ったのだが、

「恐竜は爬虫類である」

そこでスキルチェックで見つけてしまったのが「へび」。

つけたタイトルも何気なく気に入っているし運命を感じたので、
解くためのプロセスを書いてみた。

そしてランクが上がるほど、問題文が長くなり、『理解度とその世界観についていけるか』の勝負になる。
『問題を解けることが目的』なのであって、
コードの美しさ、短さを求める方には、ここから先の文章は不要です。

最後に汚いコードを載せてみる。
そして、『どこか懐かしさと寒さを感じさせるタイトルはパリピには受けない事』
を反省しつつ始めてみる。

1.問題「へび」

問題を画像で貼り付けてみる。

D7BA2F36-25C6-42F5-9BC6-C268DDF6C63E.jpeg

43193582-B07E-404F-82A8-37E5032D640E.jpeg

2.移動先の境界チェック

2.1移動範囲

一行目に表示される情報として、ヘビの移動できる縦幅と横幅がある。
この座標縦をy、横をxとすると
0<=y<=H-1,0<=x<=W-1が移動可能範囲となる。

2.2移動先に壁がないか

壁をあらわすのは#で、.の場合は移動可能。
ここでわかるのは、2.1を満たさない場合がNG,
2.2を満たさない場合もNG,両方満たす場合がOKとなる。

※余談だが、インドミナスレックスなどの恐竜様は壁ごとぶち破るので
このルールは通用しない。(俺様がルールだという奴でしょう。)

上記を判定する関数として作成したのがcheck_limit関数

3.方向と遷移する座標

ここでは、方角(向いている方角)と進む向きが与えられた時に、
遷移先の座標が現在の座標と比較して、行方向と列方向にどのくらい
移動するか、次の方角を確認する。

図で表すと下記の関係性になる

C53E1D06-9E48-4A54-A78A-5B3DADA94193.jpeg

3.1向いている方角が北(N)の時

右向きに進む時、今いる場所から右に、つまり0行1列移動する。
次の方角は、東になる。
左向きに進む場合、今いる場所から左に移動、つまり0行−1列移動する。
次の方角は西になる。
向きが指定されていない場合(方向転換する時刻でない場合がここにあたり、noneとする)、今いる場所から上に移動、つまり−1行0列移動する。
次の方角は、北になる。

3.2向いている方角が東(E)の時

右向きに進む時、今いる場所から1行(下)0列移動する
次の方角は、南になる。
左向きに進む場合、今いる場所から−1行(上)0列移動する。
次の方角は、北になる。
向きがnoneの時は、今いる場所から0行1列(右)移動する
次の方角は東になる。

この情報を元に作成したのがcheck_direction関数
(checkというよりgetがいい事に今気がつく。)

4.座標重複チェック

一度通った座標は既に蛇の胴体があるので進めない。
との事。どうやらアナコンダのような巨大な蛇を想定しているらしい。
遷移先が既に通った座標の場合、移動がストップ。

5.コード例

全体的な流れ
(1)入力値を取得
(2)方向転換する時刻配列(tarr配列)とそれに対応する方向配列(lrarr)
や座標データ取得(arr2配列)を整理する処理

(3)座標の行き止まり判定や、移動座標や移動方角取得関数の作成
(4)時刻0から99までの間、以下を繰り返す
(ⅰ)現在の座標(sy,sx)と向いてる方角と進む方向に対し、遷移先座標(ny,nx)と次の方角(next_dir)を求める
(ⅱ)遷移先座標が行き止まりかチェック(行き止まりの場合ループを抜ける)
(ⅲ)行き止まりでない場合、今まで通った座標でないかチェックする
(ⅳ)今まで通った座標でない場合、この座標を(sy,sx)とし、snake_arrに格納する。
(ⅴ)今まで通った座標の場合(snake_arrに登録されている座標の場合)ループを抜ける
(5)snake_arr配列に登録された座標のarr2データに*を代入する
(6)arr2を出力


# 自分の得意な言語で
# Let's チャレンジ!!

in1 = gets.chomp!
#puts(in1)
arr1=in1.split(' ')
#print(arr1)


row1=arr1[0].to_i
col1=arr1[1].to_i
sy=arr1[2].to_i
sx=arr1[3].to_i
num1=arr1[4].to_i


arr2 = Array.new(row1) { Array.new(col1,"") }

#print(arr2)

for i in 1..row1 do
    tmp1=gets.chomp.to_s
    for j in 1..col1 do
        arr2[i-1][j-1]=tmp1[j-1...j]
    end
end

tarr=[]
lrarr=[]


for i in 1..num1 do
    tmp1=gets.chomp.to_s
    arr3=tmp1.split(" ")
    tarr.push(arr3[0].to_i)
    lrarr.push(arr3[1])
    #in3.push(tmp1)
end



def check_limit(arr0,i,j,row1,col1)
    if i<0 or i>row1-1
        flg=1
    elsif j<0 or j>col1-1
        flg=1
    elsif arr0[i][j]=="#"
        flg=1
    else
        flg=0
    end
    return flg
end 


def check_direction(p_dir,rlstr)
    retstr=""
    if p_dir=="N"
        if rlstr=="R"
            retstr="0,1,E"
        elsif rlstr=="L"
            retstr="0,-1,W"
        else
            retstr="-1,0,N"
        end
    elsif p_dir=="S"
        if rlstr=="R"
            retstr="0,-1,W"
        elsif rlstr=="L"
            retstr="0,1,E"
        else
            retstr="1,0,S"
        end
    elsif p_dir=="E"
        if rlstr=="R"
            retstr="1,0,S"
        elsif rlstr=="L"
            retstr="-1,0,N"
        else
            retstr="0,1,E"
        end
    else
        if rlstr=="R"
            retstr="-1,0,N"
        elsif rlstr=="L"
            retstr="1,0,S"
        else
            retstr="0,-1,W"
        end
    end
    return retstr
end


def find_lr(sarr,retarr,i2,num1)
    rnum=-1
    for i in 0..num1-1 do
        if sarr[i]==i2
            rnum=i
            break
        end
    end
    return retarr[rnum]
end



snake_arr=[]
snk_str=arr1[2]+" "+arr1[3]
snake_arr.push(snk_str)
#print(snake_arr)
p_dir='N'
flg_e=0
for i in 0..99 do
    if tarr.count(i)==0
        retstr=check_direction(p_dir,"none")
        tmparr=retstr.split(",")
        ny=tmparr[0].to_i+sy
        nx=tmparr[1].to_i+sx
        next_dir=tmparr[2]
        flg=check_limit(arr2,ny,nx,row1,col1)

    else
        lrstr=find_lr(tarr,lrarr,i,num1)
        retstr=check_direction(p_dir,lrstr)
        tmparr=retstr.split(",")
        ny=tmparr[0].to_i+sy
        nx=tmparr[1].to_i+sx
        next_dir=tmparr[2]
        flg=check_limit(arr2,ny,nx,row1,col1)
    end    

    if flg==0
        sy=ny
        sx=nx
        p_dir=next_dir
        str1=sy.to_s+" "+sx.to_s
        if snake_arr.count(str1)==0
            snake_arr.push(str1)
        else
            flg_e=1
            #puts(1)
        end
    else
        flg_e=1
        #puts(2)
    end    
    if flg_e==1
        break
    end    
end    


#print(snake_arr)
#print(snake_arr.length)


for i in 0..snake_arr.length-1 do
    tmp1=snake_arr[i].to_s.split(" ")
    arr2[tmp1[0].to_i][tmp1[1].to_i]="*"
end


str1=""

for i in 0..row1-1 do
    str1=""
    for j in 0..col1-1 do
        str1=str1+arr2[i][j]
    end
    puts(str1)
end    

6.コード(改善後)

scivolaさんから頂いたコメントを基に、上記コードを修正。
rubyの利点が出せたように思う。
scivolaさん、ありがとうございます。


# 自分の得意な言語で
# Let's チャレンジ!!
in1 = gets.chomp!
#puts(in1)
arr1=in1.split(' ')
#print(arr1)

row1=arr1[0].to_i
col1=arr1[1].to_i
sy=arr1[2].to_i
sx=arr1[3].to_i
num1=arr1[4].to_i

arr2 = Array.new(row1) { Array.new(col1,"") }

#print(arr2)

for i in 1..row1 do
    tmp1=gets.chomp.to_s
    for j in 1..col1 do
        arr2[i-1][j-1]=tmp1[j-1...j]
    end
end

tarr=[]
lrarr=[]


for i in 1..num1 do
    tmp1=gets.chomp.to_s
    arr3=tmp1.split(" ")
    tarr.push(arr3[0].to_i)
    lrarr.push(arr3[1])
    #in3.push(tmp1)
end



def check_limit(arr0,i,j,row1,col1)
    if i<0 or i>row1-1
        1
    elsif j<0 or j>col1-1
        1
    elsif arr0[i][j]=="#"
        1
    else
        0
    end
end 


def check_direction(p_dir,rlstr)
    if p_dir=="N"
        if rlstr=="R"
            [0,1,"E"]
        elsif rlstr=="L"
            [0,-1,"W"]
        else
            [-1,0,"N"]
        end
    elsif p_dir=="S"
        if rlstr=="R"
            [0,-1,"W"]
        elsif rlstr=="L"
            [0,1,"E"]
        else
            [1,0,"S"]
        end
    elsif p_dir=="E"
        if rlstr=="R"
            [1,0,"S"]
        elsif rlstr=="L"
            [-1,0,"N"]
        else
            [0,1,"E"]
        end
    else
        if rlstr=="R"
            [-1,0,"N"]
        elsif rlstr=="L"
            [1,0,"S"]
        else
            [0,-1,"W"]
        end
    end
end


def find_lr(sarr,retarr,i2,num1)
    rnum=-1
    for i in 0..num1-1 do
        if sarr[i]==i2
            rnum=i
            break
        end
    end
    return retarr[rnum]
end


snake_arr=[]
snk_str=arr1[2]+" "+arr1[3]
snake_arr.push(snk_str)
#print(snake_arr)
p_dir='N'
flg_e=0
for i in 0..99 do
    if tarr.count(i)==0
        ny,nx,next_dir=check_direction(p_dir,"none")
        ny=ny+sy
        nx=nx+sx
        flg=check_limit(arr2,ny,nx,row1,col1)

    else
        lrstr=find_lr(tarr,lrarr,i,num1)
        ny,nx,next_dir=check_direction(p_dir,lrstr)
        ny=ny+sy
        nx=nx+sx
        flg=check_limit(arr2,ny,nx,row1,col1)
    end    

    if flg==0
        sy=ny
        sx=nx
        p_dir=next_dir
        str1=sy.to_s+" "+sx.to_s
        if snake_arr.count(str1)==0
            snake_arr.push(str1)
        else
            flg_e=1
            #puts(1)
        end
    else
        flg_e=1
        #puts(2)
    end    
    if flg_e==1
        break
    end    
end    


#print(snake_arr)
#print(snake_arr.length)


for i in 0..snake_arr.length-1 do
    tmp1=snake_arr[i].to_s.split(" ")
    arr2[tmp1[0].to_i][tmp1[1].to_i]="*"
end


str1=""

for i in 0..row1-1 do
    str1=""
    for j in 0..col1-1 do
        str1=str1+arr2[i][j]
    end
    puts(str1)
end    
0
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0