Code Golf:识别ascii艺术盒

时间:2010-09-10 01:56:14

标签: language-agnostic code-golf ascii-art

在做一些数据结构工作的同时提出了这个问题,虽然它可以做出很好的代码高尔夫:给定包含ascii艺术矩形的二维字符数组,生成矩形的坐标和大小列表。

  • 任何简单的可转换输入或输出格式都可以(例如:char **,字符串列表,标准输入上的行;四个整数列表,结构,固定数量+/-表示大小;等等)。
  • 同样,输出不必按任何特定顺序排列。
  • 对于无效输入或格式错误的矩形,你没有任何有用的东西,但是你不应该为输入中没有的矩形生成有效的坐标。
  • 没有两个有效的矩形共享+(尽管+可能不仅仅显示为矩形的一部分)
  • 您可以假设所有矩形至少为3x3:每一方都有-|

示例:

"        "
"  +-+ | "
"  | | \-"
"  +-+   "
(2,1;3,3)

"+--+  +--+"
"|  |  |  |"
"+--+  +--+"
(0,0;4,3), (6,0;4,3)

"  +---+  "
"->|...|  "
"  +---+  "
(2,0;5,3)

"+-+ +--+  +--+"
"| | |  |  |  |"
"+-+ |  |  + -+"
"    |  |      "
"    +--+  +-+ "
"  +--+    |   "
"  +--+    +-+ "
(0,0;3,3), (4,0;4,5) # (2,5;4,2) is fine, but not needed

10 个答案:

答案 0 :(得分:7)

Perl, 167 165 159 chars

156个字符如果你不计算stdin到@a,只需删除最后3个字符并指定一个表示你输入的字符串列表给@a)

从stdin获取输入。新线不重要,为了便于阅读而添加。请注意使用+++运算符; P

map{$l=$i++;while($c=/\+-+\+/g){$w=$+[0]-2-($x=$-[0]);
$c++while$a[$l+$c]=~/^.{$x}\|.{$w}\|/;
print"($x,$l;",$w+2,",$c)\n"if$a[$c+++$l]=~/^.{$x}\+-{$w}\+/}}@a=<>


在你接受的版本中,要自由,170个字符

map{$l=$i++;while($c=/\+-*\+/g){pos=-1+pos;$w=$+[0]-2-($x=$-[0]);
$c++while$a[$l+$c]=~/^.{$x}\|.{$w}\|/;
print"($x,$l;",$w+2,",$c)\n"if$a[$c+++$l]=~/^.{$x}\+-{$w}\+/}}@a=<>


保守你所接受的版本,177个字符

map{$l=$i++;while($c=/\+-+\+/g){$w=$+[0]-2-($x=$-[0]);
$c++while$a[$l+$c]=~/^.{$x}\|.{$w}\|/;print
"($x,$l;",$w+2,",$c)\n"if$c>1&&$a[$c+++$l]=~s/^(.{$x})\+(-{$w})\+/$1v$2v/}}@a=<>


评论版:

@a=<>;          # slurp stdin into an array of lines
$l=0;           # start counting lines from zero
map{            # for each line
    while(/\+-+\+/g){               # match all box tops
            $c=1;                           # initialize height

            # x coordinate, width of box - sides
            $w=$+[0]-2-($x=$-[0]);

            # increment height while there are inner parts
            # of a box with x and w coinciding with last top
            # (look into next lines of array)
            $c++  while $a[$l+$c]=~/^.{$x}\|.{$w}\|/;

            # if there is a box bottom on line + height
            # with coinciding x and w, print coords
            # (after incrementing height)
            print "($x,$l;",$w+2,",$c)\n"  
                    if $a[$c+++$l]=~/^.{$x}\+-{$w}\+/
    }
    $l++    # line++
}@a


大型测试用例:

+--+  +-+ +-+  +++   +---+   +-+  +-+-+  +-++-+
|SO|  | | | |  +++   |+-+|   | |  | | |  | || |
+--+  +-+-+-+  +++   ||+||   +-+  +-+-+  +-++-+
        | |          |+-+|   | |
      +-+-+-+        +---+   +-+
      | | | |
      +-+ +-+


++ +-+ ++     +-+   +- + +--+ +--+ +--+
|| +-+ ++   +-+-+   |  | |  | |    |  |
++          | |     |  | |  | |  |    |
            +-+     +--+ + -+ +--+ +--+

答案 1 :(得分:6)

Ruby - 306 260 245 228 168

# 228 chars
g=->(s,u='-'){o=[];s.scan(/\+#{u}+\+/){o<<[$`,$`+$&].map(&:size)};o}
b=t.map{|i|i.split''}.transpose.map{|s|g[s*'','\|']}
(1...t.size).map{|i|i.times{|j|(g[t[i]]&g[t[j]]).map{|x,y|p [x,j,y-x,i-j+1]if(b[x]&b[y-1]&[[j,i+1]])[0]}}}

产生

[0, 0, 3, 3]
[4, 1, 4, 3]
[10, 3, 3, 3]

表示t =

["+-+       +--+",
"| | +--+  |  |",
"+-+ |  |  + -+",
"    +--+  +-+ ",
"  +--+    | | ",
"  +--+    +-+ "]

说明:

# function returns info about all inclusions of "+---+" in string
# "  +--+ +-+" -> [[2,5],[7,9]]
g=->(s,u='-'){o=[];s.scan(/\+#{u}+\+/){o<<[$`,$`+$&].map(&:size)};o}

# mapping transposed input with this function
b=t.map{|i|i.split''}.transpose.map{|s|g[s*'','\|']}
# earlier here was also mapping original input, but later was merged with "analyse"

# "analyse"
# take each pair of lines
(1...t.size).map{|i|i.times{|j|
    # find horizontal sides of the same length on the same positions
    (g[t[i]]&g[t[j]]).map{|x,y|
        # make output if there are correct vertical sides
        p [x,j,y-x,i-j+1]if(b[x]&b[y-1]&[[j,i+1]])[0]
    }
}}

# yeah, some strange +/-1 magick included ,.)

更直接的168-chars解决方案!

t.size.times{|i|t[0].size.times{|j|i.times{|k|j.times{|l|p [l,k,j-l+1,i-k+1]if
t[k..i].map{|m|m[j]+m[l]}*''=~/^\+\+\|+\+\+$/&&t[i][l..j]+t[k][l..j]=~/^(\+-+\+){2}$/}}}}

答案 2 :(得分:6)

Perl - 223 222 216

高尔夫版本(换行符不重要):

$y=0;sub k{$s=$-[0];"($s,%i;".($+[0]-$s).",%i)"}while(<>){while(/\+-+\+/g){
if(exists$h{&k}){push@o,sprintf k,@{$h{&k}};delete$h{&k}}else{$h{&k}=[$y,2]}}
while(/\|.+?\|/g){++${$h{&k}}[1]if exists$h{&k}}++$y}print"@o\n"

较旧的脱皮版本:

# y starts at line zero.
$y = 0;

# Abuse Perl's dynamic scoping rules
# to get a key for the hash of current rectangles,
# which indexes rectangles by x and width,
# and is also used as a format string.
sub k {

    # The start of the current match.
    $s = $-[0];

    # $+[0] is the end of the current match,
    # so subtract the beginning to get the width.
    "($s,%i;" . ($+[0] - $s) . ",%i)"

}

# Read lines from STDIN.
while (<>) {

    # Get all rectangle tops and bottoms in this line.
    while (/\+-+\+/g) {

        # If line is a bottom:
        if (exists $h{&k}) {

            # Add to output list and remove from current.
            push @o, sprintf k, @{$h{&k}};
            delete $h{&k}

        # If line is a top:
        } else {

            # Add rectangle to current.
            $h{&k} = [$y, 2]

        }

    }

    # Get all rectangle sides in this line.
    while (/\|.+?\|/g) {

        # Increment the height of the corresponding
        # rectangle, if one exists.
        ++${$h{&k}}[1] if exists $h{&k}

    }

    # Keep track of the current line.
    ++$y

}

# Print output.
print join", ",@o

请注意,这不会处理矩形的 left 的垃圾垂直条,即:

   +--+  +--+
|  |  |  |  |
   +--+  +--+

两者都会错误地产生2的高度。这是因为/\|.+?\|/g模式从行的开头开始搜索。有人建议如何解决这个问题吗?

答案 3 :(得分:5)

JavaScript - 156个字符*

同样在http://jsfiddle.net/eR5ee/4/点击链接,如果使用的是Firefox或Chrome)或http://jsfiddle.net/eR5ee/5/(适用于Safari和Opera):

var A = [
    "+-+ +--+  +--+",
    "| | |  |  |  |",
    "+-+ |  |  + -+",
    "    |  |      ",
    "    +--+  +-+ ",
    "  +--+    |   ",
    "  +--+    +-+ "
    ]; // not counted

for(y=A.length;--y;)for(;m=/\+-*\+/g(A[y]);){
for(w=m[0].length,z=y;A[--z][x=m.index]+A[z][x+w-1]=="||";);
/^\+-*\+$/(A[z].substr(x,w))&&alert([x,z,w,y-z+1])}
  • 排除换行符和空白字符,这是完全没必要的。
  • 显然,Firefox和Chrome保留了第一个正则表达式的lastIndex。还需要四个字符才能使Safari和Opera无法循环。要使Internet Explorer正常工作,还需要十四个字符才能解决上述问题和“预期的功能”错误。显然,“...正则表达式的exec方法可以间接调用(使用regexp(str))”(引自Mozilla文档)不适用于IE。

如果没有矩形的底部触及任何其他矩形的顶部或底部或正负号且没有重叠,则代码会检测所有2x2和更大的矩形。

每个警告框中的数字顺序(对应于一个矩形)是顶部宽度高度。如果矩形从顶部延伸,代码会出错,但所有已经输出的坐标已经输出(来自规范:“你没有( sic )对无效输入或格式错误的矩形有用的任何东西。 ..“)

由于大多数主要的Web浏览器都实现了canvas标记,因此在几行代码中,可以在屏幕上绘制检测到的矩形。 http://jsfiddle.net/MquqM/6/适用于除Internet Explorer和Opera之外的所有浏览器。

编辑:消除了不必要的变量赋值 编辑2:避免使用完全有效的输入抛出错误( - 而不是y--),澄清代码处理的特定情况

答案 4 :(得分:4)

C( 204 186个字符)

    #include<stdio.h>
    char H=7,W=14,*S =
    "+-+ +--+  +--+"
    "| | |  |  |  |"
    "+-+ |  |  + -+"
    "    |  |      "
    "    +--+  +-+ "
    "  +--+    |   "
    "  +--+    +-+ ";
    void main(){
#define F(a,r,t)if(*c==43){while(*(c+=a)==r);t}
char*c,*o,*e=S;while(*(c=e++))
F(1,45,F(W,'|',o=c;F(-1,45,F(-W,'|',c==e-1?
printf("%i,%i %i,%i\n",(c-S)%W,(c-S)/W,(o-c)%W+1,(o-c)/W+1):0;))))
    }

字符数是main()的主体。此代码将使用 e 遍历字符串,直到它到达潜在矩形的左上角。然后,它将使用 c 检查边缘,并使用 o 跟踪右下角。

该计划的输出是:

0,0 3,3
4,0 4,5
2,5 4,2

答案 5 :(得分:3)

Scala 2.8 - 283 273 269 257

val a = Seq(
    "+-+ +--+  +--+",
    "| | |  |  |  |",
    "+-+ |  |  + -+",
    "    |  |      ",
    "    +--+  +-+ ",
    "  +--+    |   ",
    "  +--+    +-+ "
  )

// begin golf count
val (w,h) = (a(0).size-1,a.size-1)
for (
  x <- 0 to w;
  y <- 0 to h;
  u <- x+2 to w;
  v <- y+2 to h;
  if Set(a(y)(x),a(v)(x),a(y)(u),a(v)(u)) == Set(43)
  && (x+1 to u-1).forall(t => (a(y)(t)<<8|a(v)(t)) == 11565)
  && (y+1 to v-1).forall(t => (a(t)(x)<<8|a(t)(u)) == 31868)
) yield (x,y,u-x+1,v-y+1)
// end golf count

评估为:

Vector((0,0,3,3), (4,0,4,5))

for表达式求值为答案(Vector对象),这就是为什么我只计算了这个部分(删除了空格)。如果这是正确的计算方法,请告诉我。

如何运作

for表达式生成所有可能矩形的坐标(实际上,只有> = 3x3)。通过查找+, - 和|来过滤这些坐标在所有矩形的边缘和角落(if表达式的for部分)。

答案 6 :(得分:3)

Python 2.6 - 287 263 254

a = [
    "+-+ +--+  +--+",
    "| | |  |  |  |",
    "+-+ |  |  + -+",
    "    |  |      ",
    "    +--+  +-+ ",
    "  +--+    |   ",
    "  +--+    +-+ "
    ]

l=len
r=range
w,h=l(a[0]),l(a)
[(x,y,u,v)for x in r(0,w)for y in r(0,h)for u in r(x+2,w)for v in r(y+2,h)if a[y][x]==a[v][x]==a[y][u]==a[v][u]=='+' and a[y][x+1:u]+a[v][x+1:u]=="-"*2*(u-x-1)and l([c for c in r(y+1,v-y)if a[c][x]==a[c][u]=='|'])==v-y-1]

评估为:

[(0, 0, 3, 3), (4, 0, 4, 5)]

答案 7 :(得分:3)

Python 2.6(251个字符)

我来晚了一点,无论如何,有点乐趣。 Python,使用正则表达式。要保存 print 语句并保持比Fredb219短,如果您将其作为脚本运行,则不会打印任何内容,但是在解释器中一次键入一行它将显示结果。不是很稳固,它不会处理嵌套框,也不会比DavidX给出的更复杂。没有做过测试,但我认为如果发生“奇怪”的事情,它可能会以错误的顺序显示结果。

import re
l,a,o=s.index("\n")+1,re.finditer,sorted
o(o(set((m.start(),m.end())for m in a(r'\+-* *-*\+',s)for n in a(r'\|.+\|',s)if(m.start()%l,m.end()%l)==(n.start()%l,n.end()%l)if m.start()+l==n.start()or m.start()-l==n.start())),key=lambda x:x[0]%l)

输入是一个单独的字符串,行(所有相同的长度)由换行符分隔。结果是每个“好”框的顶部和底部的字符串切片,从左上角开始。它还允许“破损”的盒子(即在一侧的中间有一些空间,而不是没有一个整个侧面)。这只是一种修复不良行为的方法,可以创建许多全新的副作用! :-)

输入:

>>>s = """+-+ +--+  +--+
| | |  |  |  |
+-+ |  |  + -+
    |  |      
    +--+  +-+ 
  +--+    |   
  +--+    +-+ """

或:

>>>s = "+-+ +--+  +--+\n| | |  |  |  |\n+-+ |  |  + -+\n    |  |      \n    +--+  +-+ \n  +--+    | \n  +--+    +-+ "

然后:

>>>import re
>>>l,a,o=s.index("\n")+1,re.finditer,sorted
>>>o(o(set((m.start(),m.end())for m in a(r'\+-* *-*\+',s)for n in a(r'\|.+?\|',s)if(m.start()%l,m.end()%l)==(n.start()%l,n.end()%l)if m.start()+l==n.start()or m.start()-l==n.start())),key=lambda x:x[0]%l)

输出:

[(0, 3), (30, 33), (4, 8), (64, 68), (10, 14), (40, 44)]

所以(0,3)同一个盒子的第一个盒子(30,33)的顶部,(4,8)第二个盒子的顶部,依此类推。

答案 8 :(得分:2)

F#,297个字符

有点跛,但很简单。

let F a=
 for x=0 to Array2D.length1 a-1 do
  for y=0 to Array2D.length2 a-1 do
  if a.[x,y]='+' then
   let mutable i,j=x+1,y+1
   while a.[i,y]<>'+' do i<-i+1
   while a.[x,j]<>'+' do j<-j+1
   printfn"(%d,%d;%d,%d)"x y (i-x+1)(j-y+1)
   a.[i,y]<-' '
   a.[x,j]<-' '
   a.[i,j]<-' '

寻找加号。找到它右边的一个。找到它下面的那个。打印出这个矩形的信息,并“清空”我们已经使用过的那些。由于每个加号只是一个有效矩形的一部分,所以我们需要这样做。

答案 9 :(得分:2)

XQuery(304个字符)

这是我的解决方案:

declare variable $i external;let$w:=string-length($i[1]),$h:=count($i)for$y in 1 to$h,$x in 1 to$w,$w in 2 to$w+1 -$x,$h in 1 to$h where min(for$r in (0,$h),$s in 1 to$h return (matches(substring($i[$y+$r],$x,$w),'^\+-*\+$'),matches(substring($i[$y+$s],$x,$w),'^|.*|$')))return ($x -1,$y -1,$w,$h+1,'')

您可以通过将变量$i设置为输入行来运行此操作(使用 XQSharp

>XQuery boxes.xq "i=('  +-+','+-+-+','| |  ','+-+  ')" !method=text

2 0 3 2  0 1 3 3

我想有人可能会说declare variable $i external;只是设置输入,因此不会添加到计数中,在这种情况下 275个字符