レースカーのトラックを作りましょう!


19

前書き

私のはレースカーのトラックを作りたいと思っています。彼女は、一緒になってトラックを形成する木製の部品を持っています。各パーツは正方形で、異なる形状が含まれています。パイプ描画文字を使用して説明します。

  • :垂直に進む道路
  • :水平に進む道路
  • :方向に曲がる道路
  • :地下道のある橋

不思議なことに、tジャンクションはありません。

レースカートラックの例を次に示します。

┌─┐
│ │┌─┐
│ └┼─┘
└──┘

有効なレースカートラックのルールは次のとおりです。

  • どこにも行かない道路はありません。
  • ループを形成する必要があります(すべてのピースが同じループの一部でなければなりません)。
  • 橋/地下道では、曲がることはできません(そのため、まっすぐに通らなければなりません)。

残念ながら、raceと私が持っているレースカーのトラックは限られています。しかし、我々は間違いなくそれらをすべてトラックで使用したいと考えています。在庫にあるピースのリストを指定して、それらのピースをすべて使用するレースカートラックを出力するプログラムを作成します。

入力説明

STDIN、コマンドライン引数、ファイル読み取り、またはユーザー入力関数(raw_inputまたはなどprompt)を介して入力を受け取りたい。入力は、次の形式のコンマ区切りの正の整数です。

│,─,┌,┐,└,┘,┼

ここで、それぞれが特定のピースの量を表します。したがって、たとえば入力:

1,1,1,1,1,1,1

それぞれのピースが1つあることを意味します

出力の説明

上記のパイプ描画文字を使用して、レースカートラックを出力します。レースカートラックは、入力で指定された各ピースの数を正確に使用する必要があります。これ以上でもそれ以下でもありません。すべての入力に対して少なくとも1つの有効なレースカートラックがあります。

入力と出力の例

入力: 3,5,2,2,2,2,1

可能な出力:

┌─┐
│ │┌─┐
│ └┼─┘
└──┘

入力: 0,0,1,4,4,1,3

可能な出力:

 ┌┐
 └┼┐
  └┼┐
   └┼┐
    └┘

出力する必要がありますか?または、理論的には出力を提供する必要があるだけですか?
-Sumurai8

@ Sumurai8「理論的に」出力するとはどういう意味ですか?非常に長い間終了しないが、最終的には出力されるプログラムを意味しますか?
アブサン

1
レースピースと空の正方形で満たされたnxnの正方形のフィールドを作成でき、レーストラックであるものが見つかるまで順列を生成できます。それは、数個のタイル以上のものには永遠にかかります。
-Sumurai8

4
@ Sumurai8わかりました、わかりました。私は、チャレンジで示した小さな値の入力に対して、プログラムが宇宙の熱死の前に出力を出すことを望みます。
アブサン

4
あなたのは十分に忍耐強くありません!:P
サムライ

回答:


4

ルビー664 671 677 687 701(678バイト)

_={│:[1,4],─:[2,8],┌:[4,8],┐:[4,2],└:[1,8],┘:[1,2],┼:[1,4,2,8]}
s=->a,l,b{l==[]&&a==[]?b:(l.product(l).any?{|q,r|q,r=q[0],r[0];(q[0]-r[0])**2+(q[1]-r[1])**2>a.size**2}?!0:(w,f=l.pop
w&&v=!a.size.times{|i|y=_[x=a[i]]
f&&y&[f]==[]||(k=l.select{|p,d|w!=p||y&[d]==[]}
(y-[f]).map{|d|z=[w[0]+(d<2?-1:(d&4)/4),w[1]+(d==2?-1:d>7?1:0)]
g=d<3?d*4:d/4
b[z]?_[b[z]]&[g]!=[]||v=0:k<<[z,g]}
v||r=s[a[0...i]+a[i+1..-1],k,b.merge({w=>x})]
return r if r)}))}
c=eval"[#{gets}]"
r=s[6.downto(0).map{|i|[_.keys[i]]*c[i]}.flatten,[[[0,0],nil]],{}]
h=j=k=l=0
r.map{|w,_|y,x=w
h>x&&h=x
j>y&&j=y
k<x&&k=x
l<y&&l=y}
s=(j..l).map{|_|' '*(k-h+1)}
r.map{|w,p|y,x=w
s[y-j][x-h]=p.to_s}
puts s

これは私が思いついた最短のプログラムではありませんが、実行速度のために簡潔さをいくらか犠牲にしました。

ここでプログラムを試すことができます。イデオンには実行時間の制限があるため、約12個を超える入力で構成される入力の場合、プログラムはおそらくタイムアウトします。

プログラム用のテストスイートもあります。上記の時間制限のため、最後の2つのテストはideoneで無効になっていることに注意してください。これらのテストを有効にするにはx_、名前からプレフィックスを削除します。

プログラムは、深さ優先検索を使用して解決策を見つけます。ピースを一度に1つずつ配置し、ルーズエンドを追跡します。ルーズ(接続されていない)端がなくなり、すべてのピースが配置されると、検索は停止します。

これは、無料のプログラムです。

N, W, S, E = 1, 2, 4, 8

# given a direction, find the opposite
def opposite (dir)
  dir < 3 ? dir * 4 : dir / 4
end

# given a set of coordinates and a direction,
# find the neighbor cell in that direction
def goto(from, dir)
  y, x = from

  dx = case dir
  when W then -1
  when E then 1
  else 0
  end

  dy = case dir
  when N then -1
  when S then 1
  else 0
  end

  [y+dy, x+dx]
end

CONNECTIONS = {
  ?│ => [N, S],
  ?─ => [W, E],
  ?┌ => [S, E],
  ?┐ => [S, W],
  ?└ => [N, E],
  ?┘ => [N, W],
  ?┼ => [N, S, W, E], 
}

BuildTrack =-> { 
  piece_types = CONNECTIONS.keys
  piece_counts = gets.split(?,).map &:to_i

  pieces = 6.downto(0).map{|i|piece_types[i]*piece_counts[i]}.join.chars

  def solve (available_pieces, loose_ends=[[[0,0],nil]], board={})

    return board if loose_ends==[] and available_pieces==[]

    # optimization to avoid pursuing expensive paths
    # which cannot yield a result.
    # This prunes about 90% of the search space
    c = loose_ends.map{ |c, _| c }
    not_enough_pieces = c.product(c).any? { |q, r| 
      ((q[0]-r[0])**2+(q[1]-r[1])**2) > available_pieces.size**2
    }
    return if not_enough_pieces

    position, connect_from = loose_ends.pop

    return unless position

    available_pieces.size.times do |i|
      piece = available_pieces[i]

      remaining_pieces = available_pieces[0...i] + available_pieces[i+1..-1]

      piece_not_connected_ok = connect_from && CONNECTIONS[piece] & [connect_from] == []
      next if piece_not_connected_ok

      new_loose_ends = loose_ends.select  { |pos, dir| 
        # remove loose ends that may have been 
        # fixed, now that we placed this piece
        position != pos || CONNECTIONS[piece] & [dir] == []
      }

      invalid_placement = false

      (CONNECTIONS[piece]-[connect_from]).map do |dir|
        new_pos = goto(position, dir)
        new_dir = opposite(dir)

        if board[new_pos]
          if CONNECTIONS[board[new_pos]] & [new_dir] != []
            # do nothing; already connected
          else
            # going towards an existing piece
            # which has no suitable connection
            invalid_placement = true
          end
        else
          new_loose_ends << [new_pos, new_dir]
        end
      end

      next if invalid_placement

      new_board = board.merge({position => piece})

      result = solve(remaining_pieces, new_loose_ends, new_board)
      return result if result
    end
    nil
  end

  def print_board board
    min_x = min_y = max_x = max_y = 0

    board.each do |position, _|
      y, x = position
      min_x = [min_x, x].min
      min_y = [min_y, y].min
      max_x = [max_x, x].max
      max_y = [max_y, y].max
    end

    str = (min_y..max_y).map{|_|
      ' ' * (max_x - min_x + 1)
    }

    board.each do |position, piece|
      y, x = position
      str[y-min_y][x-min_x] = piece
    end
    puts str
  end

  print_board(solve(pieces))
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.