Đầu tiên, một số thuật ngữ ( nguồn ):
- Một mái hông được (trích dẫn Wikipedia) "một loại mái nhà, nơi tất cả các bên dốc xuống các bức tường, thường là với một độ dốc khá nhẹ nhàng"
- Độ dốc là một mặt phẳng là một phần của mái nhà
- Một sườn núi là một cạnh nơi hai sườn mái đối diện gặp nhau
- Hông là một cạnh lồi, nơi hai sườn thuộc các bức tường vuông góc gặp nhau
- Một thung lũng là một cạnh lõm, nơi hai sườn dốc của các bức tường vuông góc gặp nhau
- Hông và thung lũng sẽ được gọi chung là các cạnh chéo.
Đầu vào có thể:
** * ***
********
** * **
Đầu ra tương ứng:
+-------+ +---+ +-----------+
|\ /| |\ /| |\ /|
| \ / | | V | | \ ^---< |
| \ / | | | | | \ / \ \|
+---+ V +---+ | +---+ X +---+
|\ \ | / \|/ \ / \ |
| >---> | <-------X-------V > |
|/ / | \ /|\ /| |
+---+ ^ +---+ | +-------+ | +---+
| / \ | | | | | |/ /|
| / \ | | ^ | | /---< |
|/ \| |/ \| |/ \|
+-------+ +---+ +-------+
Một vài trường hợp thử nghiệm nữa:
** *** * * * *
* *** *****
** ***** *****
* * * *** *** ***
* **** * * *
Đầu ra tương ứng:
+-------+ +-----------+ +---+ +---+ +---+ +---+
|\ /| |\ /| |\ /| |\ /| |\ /| |\ /|
| \---< | | >-------< | | V | | V | | V | | X |
| |\ \| |/ \| | | | | | | | | | |/ \|
| | +---+ +-----------+ +---+ | +---+ | | +-----------+ | | +---+
| | | |\ \|/ /| | |/ \| |
| ^ | | \ V / | | < > |
|/ \| | \ / | | \ / |
+---+ +-------+ +---+ \ / +---+ | \-----------/ |
|\ /| |\ \ \ / / /| | |\ /| |
| >---/ | | >---> X <---< | | | \ / | |
|/ /| | |/ / / \ \ \| | | \ / | |
+---+ +---+ +---+ | | +---+ / \ +---+ +---+ ^ +---+ ^ +---+
|\ /| |\ /| | | | | / \ | |\ \ / \ | | / \ / /|
| V | | V | | | | | / ^ \ | | >---V > | | < V---< |
| | | | | | | | | |/ /|\ \| |/ /| | | |\ \|
| | | | | +-------+ | | +---+ | +---+ +-------+ | | | | +-------+
| | | | |/ \| | | | | | | | | | |
| ^ | | /-----------\ | | ^ | | ^ | | ^ |
|/ \| |/ \| |/ \| |/ \| |/ \|
+---+ +---------------+ +---+ +---+ +---+
Đầu vào của bạn sẽ là một bitmap - một mảng 2D pixel vuông - của khu vực cần được che bởi mái nhà. Bạn có thể cho rằng ranh giới của khu vực này sẽ là một đường cong Jordan - nghĩa là liên tục và không tự giao nhau - nghĩa là khu vực mái sẽ liên tục, không có lỗ hổng và sẽ không bao giờ có bốn bức tường gặp nhau tại một điểm. Các định dạng đầu vào hợp lệ bao gồm một chuỗi đơn với các dấu phân cách dòng mới, danh sách các chuỗi và một mảng 2D của ký tự hoặc booleans.
Các quy tắc xây dựng mái nhà là:
- Mỗi đoạn thẳng của khu vực mái (từ đó được gọi là tường) sẽ có chính xác một độ dốc liền kề. Độ dốc sẽ tăng lên từ tường. Mỗi độ dốc phải có ít nhất một bức tường liền kề và tất cả các bức tường liền kề với một độ dốc phải được đặt thẳng hàng.
- Tất cả các độ dốc phải có cùng một góc (khác không) so với bề mặt ngang. Đó là, họ phải có cùng một sân.
- Các sườn sẽ tạo thành một bề mặt có ranh giới là ranh giới của khu vực mái. Đó là, không có bề mặt nào khác ngoài các sườn dốc có thể được sử dụng.
- Bất kỳ kịch bản nào có nhiều hơn một giải pháp (tối đa tỷ lệ dọc) được cho phép bởi đặc tả này đều được coi là một lỗi trong đặc tả. Bất kỳ sửa chữa áp dụng hồi tố.
Tương tự, mái nhà có thể được xác định theo quy tắc rằng mỗi điểm của mái nhà được đặt càng cao càng tốt mà không vượt quá độ dốc tối đa cho mái đó như được đo bằng khoảng cách Ch Quashev trong chế độ xem từ trên xuống.
Đầu ra của bạn sẽ là một biểu diễn nghệ thuật ASCII của mái nhà - hoặc là một chuỗi chứa các ký tự dòng mới hoặc một chuỗi các chuỗi, mỗi chuỗi biểu thị một dòng đầu ra. Mái nhà sẽ được hiển thị ở chế độ xem từ trên xuống theo tỷ lệ 4x - nghĩa là, mỗi ô vuông của sơ đồ tầng sẽ ảnh hưởng đến một khu vực 5x5 của đầu ra sao cho các góc của khu vực 5x5 này được chia sẻ với các ô vuông lân cận (sao cho mỗi ô vuông ký tự góc bị ảnh hưởng bởi bốn ô vuông đầu vào khác nhau), như được chỉ ra bởi đầu ra ví dụ. Khoảng trắng thêm được cho phép miễn là hình dạng đầu ra được giữ nguyên. Các ký tự trong đầu ra sẽ là:
- một dấu hiệu dòng mới do môi trường xác định sẽ được sử dụng (thường là U + 000A, U + 000D hoặc một cặp cả hai) nếu đầu ra ở dạng một chuỗi đơn
(Không gian U + 0020) biểu thị một điểm bên ngoài khu vực có mái hoặc nội thất điểm đến độ dốc
+
(Dấu cộng U + 002B) biểu thị một điểm có hai bức tường vuông góc liền kề với nó-
(Dấu gạch nối U + 002D) đại diện cho một bức tường hoặc một sườn núi được định hướng theo chiều ngang (đông-tây)/
(U + 002F solidus) đại diện cho hông hoặc thung lũng được định hướng từ đông bắc sang đông nam hoặc một điểm tiếp giáp với hai trong số đó<
(Dấu nhỏ hơn U + 003C) biểu thị một điểm có hai cạnh chéo tiếp giáp với nó ở phía đông>
(Dấu lớn hơn U + 003E) biểu thị một điểm có hai cạnh chéo tiếp giáp với nó ở phía tây\
(U + 005C solidus ngược) đại diện cho một hông hoặc một thung lũng được định hướng từ tây bắc sang đông nam, hoặc một điểm tiếp giáp với hai trong số đó^
(Dấu thập phân U + 005E) biểu thị một điểm có hai cạnh chéo tiếp giáp với nó ở phía namV
(U + 0056 chữ hoa Latin v) đại diện cho một điểm có hai cạnh chéo tiếp giáp với nó ở phía bắcX
(U + 0058 chữ hoa Latin x) đại diện cho một điểm có các đường chéo chéo liền kề với nó ở cả bốn phía|
(Thanh dọc U + 007C) đại diện cho một bức tường hoặc một sườn núi được định hướng theo chiều dọc (bắc-nam)
Lưu ý rằng không thể có một số lẻ các cạnh đường chéo kết thúc tại cùng một điểm (ngoại trừ trên các bức tường). Chúng ta có thể hình dung rằng bằng cách phân vùng lân cận của mỗi điểm thành dốc phía bắc + dốc phía nam và vào sườn phía đông + dốc phía tây. Ranh giới giữa cả hai phân vùng phải bao gồm các cạnh chéo.
Nếu môi trường của bạn sử dụng mã hóa ký tự không tương thích với ASCII, bạn có thể sử dụng các ký tự tương đương (cùng glyph hoặc gần nhất có sẵn) trong mã hóa ký tự mà môi trường của bạn sử dụng.
Việc triển khai tham chiếu (xấu xí) sau đây trong Ruby là quy phạm đối với đầu ra không phải khoảng trắng. Đặc biệt lưu ý render
phương pháp:
def pad ary
row = ary.first.map{-1}
([row] + ary + [row]).map{|r| [-1] + r + [-1]}
end
def parse str
str.split("\n").map{|r| r.chars.map(&{" " => -1, "*" => Float::INFINITY})}
end
def squares ary, size
ary.each_cons(size).map do |rows|
rows.map{|row| row.each_cons(size).to_a}.transpose
end
end
def consmap2d ary, size
squares(ary, size).map{|sqrow| sqrow.map{|sq| yield sq}}
end
def relax ary
loop do
new = consmap2d(pad(ary), 3){|sq| sq[1][1] == -1 ? -1 : sq.flatten.min + 1}
return new if new == ary
ary = new
end
end
def semidouble ary, op
ary.zip(ary.each_cons(2).map{|r1,r2|r1.zip(r2).map(&op)}).flatten(1).compact.transpose
end
def heightmap str
relax(semidouble(semidouble(semidouble(semidouble(pad(parse str),:max),:max),:min),:min))
end
def render heightmap
puts consmap2d(heightmap, 3){|sq|
next " " if sq[1][1] == -1
hwall = sq[0][1] == -1 || sq[2][1] == -1
vwall = sq[1][0] == -1 || sq[1][2] == -1
next "+" if hwall && vwall
next "-" if hwall
next "|" if vwall
next "+" if sq.flatten.min == -1
nws = sq[0][1] == sq[1][0]
nes = sq[0][1] == sq[1][2]
sws = sq[2][1] == sq[1][0]
ses = sq[2][1] == sq[1][2]
next "X" if nws && nes && sws && ses
next "V" if nws && nes
next "^" if sws && ses
next ">" if nws && sws
next "<" if nes && ses
next "/" if nes && sws
next "\\" if nws && ses
next " " if sq[0][1] != sq[2][1] || sq[1][0] != sq[1][2]
next "|" if sq[0][1] == sq[1][1]
next "-" if sq[1][0] == sq[1][1]
??
}.map(&:join)
end
render heightmap $<.read if __FILE__ == $0
*
. Nếu không thì có lẽ là đủ.
[[0,1,1],[1,0,1],[1,1,1]]
đầu vào hợp lệ? (Đầu vào không có lỗ của người Viking, nhưng có một góc khó gần ngã tư.)