x、y座標使ったプログラミング問題を解いて思ったこと


メモがてらの投稿です。

よく、プログラミング問題を解く時に、x、y座標を使って解くやつありますよね。
以下みたいな感じで、あーだこーだするやつです。(雑

# #を囲うにはいくつの柵が必要?みたいな問題だとして

# 横の数と縦の数が宣言されて
6 4

# 1列ずつ値がくる
####..
..#.#.
#.#.#.
.##..#

最初、x、y座標使ってなんなん、どうやって求めるんや、、意味不...と思ってましたが、
ちょっとずつ紐解けてきました。

今回は、自分なりにx、y座標の求め方やそれを使った応用などをまとめました。
まだまだ自分も学習中なので、よりよい方法などございましたらコメントいただけると幸いです!

なお、今回はrubyを使って記述しています。

x、y座標の求め方

結論、配列を二重にし、ループ処理を使います。
こんな感じ。

fields = [
  ['#', '#', '#', '#', '.', '.'],
  ['.', '.', '#', '.', '#', '.'],
  ['#', '.', '#', '.', '#', '.'],
  ['.', '#', '#', '.', '.', '#'],
]

fields.each_with_index do |field, y|
  field.each_with_index do |f, x|
  end
end 

こうすることで、ループを使い添字を取得できます。

処理を説明していくと、
まず1回目のループ処理でfields内の各配列の添字を取得できます。

fields.each_with_index do |field, y|


これがいわゆるy軸の値です。

次に、各配列をさらにループします。
同様にこちらも、添字を取得できます。

field.each_with_index do |f, x|


これがx軸の値です。

これらを使って、#のx、y座標を取得するならば
こんな感じかと思います。

hash_places = []

fields = [
  ['#', '#', '#', '#', '.', '.'],
  ['.', '.', '#', '.', '#', '.'],
  ['#', '.', '#', '.', '#', '.'],
  ['.', '#', '#', '.', '.', '#'],
]

fields.each_with_index do |field, y|
  field.each_with_index do |a, x|
    hash_places << { x: x, y: y} if a == '#'
  end
end

# hash_placesの中身
{:x=>1, :y=>0}
{:x=>2, :y=>0}
{:x=>3, :y=>0}
{:x=>2, :y=>1}
{:x=>4, :y=>1}
{:x=>0, :y=>2}
{:x=>2, :y=>2}
{:x=>4, :y=>2}
{:x=>1, :y=>3}
{:x=>2, :y=>3}
{:x=>5, :y=>3}

x、y座標の値のリストができました。

隣接関係を調べる

取得した値を使用して、上下左右に同じ値があるか、調べることができます。
方法は+1 -1を使用するだけです。

# 左の値を確認したいならば、x軸 - 1
# 右ならば、x軸 + 1
# 上ならば、y軸 - 1
# 下ならば、y軸 + 1

これを使って、先程のhash_placesから上下左右に#があるか調べてみると、
こんな感じになります。(ちょっと汚いコードですいません。。)

def is_neighbors(place, fields)
  top = place[:y] - 1 == -1 ? 'NONE' : { x: place[:x], y: place[:y] - 1 }
  is_top = top != 'NONE' && (fields[top[:y]][top[:x]] == fields[place[:y]][place[:x]])

  right = place[:x] + 1 == fields[0].length ? 'NONE' : { x: place[:x] + 1, y: place[:y] }
  is_right = right != 'NONE' && (fields[right[:y]][right[:x]] == fields[place[:y]][place[:x]])

  bottom = place[:y] + 1 == fields.length ? 'NONE' : { x: place[:x], y: place[:y] + 1 }
  is_bottom = bottom != 'NONE' && (fields[bottom[:y]][bottom[:x]] == fields[place[:y]][place[:x]])

  left = place[:x] - 1 == -1 ? 'NONE' : { x: place[:x] - 1, y: place[:y] }
  is_left = left != 'NONE' && (fields[left[:y]][left[:x]] == fields[place[:y]][place[:x]])

  answer = {
    is_top: is_top,
    is_right: is_right,
    is_bottom: is_bottom,
    is_left: is_left,
  }
end

hash_places.each_with_index do |place, i|
  puts "#{i + 1}番目: #{is_neighbors(place, fields)}"
end
# 戻り値
1番目: {:is_top=>false, :is_right=>true, :is_bottom=>false, :is_left=>false}
2番目: {:is_top=>false, :is_right=>true, :is_bottom=>false, :is_left=>true}
3番目: {:is_top=>false, :is_right=>true, :is_bottom=>true, :is_left=>true}
4番目: {:is_top=>false, :is_right=>false, :is_bottom=>false, :is_left=>true}
5番目: {:is_top=>true, :is_right=>false, :is_bottom=>true, :is_left=>false}
6番目: {:is_top=>false, :is_right=>false, :is_bottom=>true, :is_left=>false}
7番目: {:is_top=>false, :is_right=>false, :is_bottom=>false, :is_left=>false}
8番目: {:is_top=>true, :is_right=>false, :is_bottom=>true, :is_left=>false}
9番目: {:is_top=>true, :is_right=>false, :is_bottom=>false, :is_left=>false}
10番目: {:is_top=>false, :is_right=>true, :is_bottom=>false, :is_left=>false}
11番目: {:is_top=>true, :is_right=>false, :is_bottom=>false, :is_left=>true}
12番目: {:is_top=>false, :is_right=>false, :is_bottom=>false, :is_left=>false}

各値を上下左右で計算しています。
一番外側にいた場合は、隣の値がないので、
その時はNONEを返しています。

左と上は、-1になることはないので、それを条件分岐に使用しています。
右と下は、要素の長さを超えることはないので、それを条件分岐に使用しています。

参考

each_with_indexの使い方