Code Golf: распознавать арт-боксы ascii

Придумал это некоторое время назад, выполняя некоторую работу со структурой данных, хотя это было бы неплохим кодом для гольфа: учитывая двумерный массив символов, содержащий прямоугольники ascii art, создайте список координат и размеров для прямоугольников.

  • Подойдет любой тривиально конвертируемый формат ввода или вывода (например: 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

person Community    schedule 10.09.2010    source источник
comment
Не указано. Может ли знак «+» быть частью нескольких прямоугольников? Имеет ли значение порядок вывода?   -  person Brian    schedule 10.09.2010
comment
Лучше было бы читать из stdin и писать в stdout (типично для кода гольфа).   -  person Brian    schedule 10.09.2010
comment
@Brian, 1a: нет, исправлено; 1b: нет, трижды изменить выходной формат можно; 2: я хотел сосредоточиться на алгоритме, а не на вводе-выводе.   -  person David X    schedule 10.09.2010
comment
@Mark, у соседних прямоугольников будет (на самом деле 2) +, который является их частью, так что нет. Я собираюсь сказать это до реализации, хотите ли вы обрабатывать вложенные, но это не должно быть слишком сложно разрешить.   -  person David X    schedule 10.09.2010
comment
Могут ли большие прямоугольники содержать маленькие прямоугольники?   -  person Brian    schedule 13.09.2010
comment
@Brian, если они это сделают, вам не нужно их узнавать, поскольку его реализация определяет, обрабатываете ли вы вложенные прямоугольники. (Я сомневаюсь, что есть какие-то алгоритмы, которые от этого выиграли бы.)   -  person David X    schedule 14.09.2010
comment
Можно ли использовать аналоговые литералы (xs4all.nl/~weegen/eelis/analogliterals.xhtml < / а>)?   -  person dan04    schedule 15.09.2010
comment
@ user287586, конечно, но они учитываются при подсчете символов, поэтому я сомневаюсь, что это того стоит.   -  person David X    schedule 15.09.2010
comment
Ascii art? Вы хотите, чтобы мы запрограммировали компьютер на искусствоведа ?! <ГРАММ>   -  person Loren Pechtel    schedule 17.09.2010
comment
@ Лорен, ну конечно! Все знают, что любой компьютер, предоставленный самим себе, самопроизвольно разовьет разум, поэтому давайте позаботимся о том, чтобы он раздражал не нас, а ученых-гуманитарных специалистов!   -  person David X    schedule 17.09.2010


Ответы (10)


Perl, 167 165 159 символов

(156 символов, если вы не учитываете прихлебывание stdin в @a, просто удалите последние 3 символа и назначьте список строк, представляющих ваш ввод, для @a)

Получает входные данные со стандартного ввода. Новые строки не имеют значения, добавлены для удобства чтения. Обратите внимание на использование оператора +++; 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|  | | | |  +++   |+-+|   | |  | | |  | || |
+--+  +-+-+-+  +++   ||+||   +-+  +-+-+  +-++-+
        | |          |+-+|   | |
      +-+-+-+        +---+   +-+
      | | | |
      +-+ +-+


++ +-+ ++     +-+   +- + +--+ +--+ +--+
|| +-+ ++   +-+-+   |  | |  | |    |  |
++          | |     |  | |  | |  |    |
            +-+     +--+ + -+ +--+ +--+
person Community    schedule 14.09.2010
comment
У меня нет интерпретатора Perl для тестирования. Итак, поясните, где же инициализация $i? Позволяет ли Perl помнить, что все переменные в начале 0? - person Nakilon; 20.09.2010
comment
some_loop_construct {$ l = $ i ++; ...} - моя версия $ l = 0; some_loop_construct {...; $ l ++} для гольфа. В Perl переменные имеют undef до тех пор, пока им не будут присвоены значения, в результате чего координаты вида (1,0; 0,2) будут печататься как (1,;, 2) (undef обрабатывается как 0, когда используется как число, и как пустая строка при использовании в качестве строки). - person ninjalj; 20.09.2010

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

Обратите внимание, что это не обрабатывает нежелательные вертикальные полосы слева от прямоугольников, то есть:

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

Неправильно даст высоту 2 для обоих. Это потому, что шаблон /\|.+?\|/g начинает поиск с начала строки. У кого-нибудь есть предложение, как это исправить?

person Community    schedule 13.09.2010
comment
Добавление нескольких . в начало шаблона /\|.+?\|/g должно исправить | обработку мусора, но я недостаточно хорошо знаю Perl, чтобы понять, как это сделать. - person David X; 14.09.2010
comment
Сохранено несколько символов, воспользовавшись слабыми ограничениями вывода. - person Jon Purdy; 14.09.2010

Рубин - 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-символьное решение!

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}$/}}}}
person Community    schedule 11.09.2010
comment
Вы можете объяснить, что происходит в этом коде? Кроме того, для чего нужен синтаксис ->? Это способ объявить анонимную функцию? - person barjak; 13.09.2010
comment
Разве вы не можете определить функцию (например, map) в переменной в Ruby? - person chelmertz; 13.09.2010
comment
@barjak: 1) f (или g в текущей версии) создает список +---+ подстрок в строке; программа генерирует эти карты для исходного ввода и транспонирует, а затем анализирует их 2) да, это анонимная функция. f=->{} просто короче def f end - person Nakilon; 19.09.2010

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 сохраняют последний индекс первого регулярного выражения. Требуется еще четыре символа, чтобы Safari и Opera не попадали в бесконечный цикл. Чтобы Internet Explorer заработал, потребуется еще четырнадцать символов, чтобы исправить как указанную выше ошибку, так и ошибку «Ожидаемая функция». Очевидно, «... метод exec регулярного выражения может быть вызван ... косвенно (с regexp(str))» (цитируется из документации Mozilla) не относится к IE.

Код обнаруживает все прямоугольники 2x2 и больше, если ни один из них не касается ни одного прямоугольника сверху или снизу, либо знака плюс или минус и ни один из них не перекрывается.

Порядок номеров в каждом окне предупреждения (который соответствует прямоугольнику): левый, верх, ширина, высота . Код выдает ошибку, если прямоугольник выходит за пределы вершины, но все необходимые координаты уже были выведены (из спецификации: «Вам не нужно (sic) ничего полезного для недопустимого ввода или искаженных прямоугольников. .. ")

Поскольку большинство основных веб-браузеров реализуют тег холста, в нескольких строках кода можно рисовать обнаруженные прямоугольники на экране. http://jsfiddle.net/MquqM/6/ работает во всех браузерах, кроме Internet Explorer и Opera.

Изменить: исключено ненужное присвоение переменных. Изменить 2: избегать ошибок с полностью допустимым вводом (--y вместо y--), прояснить конкретные случаи, которые обрабатывает код

person Community    schedule 30.09.2010
comment
Разве это не должно быть --y вместо y--? - person ninjalj; 30.09.2010
comment
Либо работает; это просто зависит от того, согласен ли кто-то с кодом, выдающим ошибку даже при правильном вводе. Я меняю приведенный выше код, чтобы этого избежать. - person PleaseStand; 30.09.2010

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
person Community    schedule 15.09.2010

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)]
person Community    schedule 13.09.2010
comment
+1 за то, что у меня хороший вкус к использованию того же алгоритма, что и у меня! - person barjak; 13.09.2010
comment
Скобки можно опускать w,h = len(a[0]),len(a). назначить r=range и вместо этого вызвать r() ... и это вроде как неполно ... где код о a? - person st0le; 13.09.2010
comment
a то же самое, что и для кода Scala. Я все это исправляю, спасибо. - person Niavok; 13.09.2010
comment
Вы можете связать сравнения, например '+'==a[y][x]==a[v][x]==a[y][u]==a[v][u] сохраняет несколько символов - person John La Rooy; 14.09.2010

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), поэтому я засчитал только эту часть (пробелы удалены). Сообщите мне, правильный ли это способ подсчета.

Как это работает

Координаты всех возможных прямоугольников (на самом деле только> = 3x3) генерируются выражением for. Эти координаты фильтруются по знакам +, - и | по краям и углам всех прямоугольников (часть if выражения for).

person Community    schedule 11.09.2010
comment
Я думаю, что есть способ упростить узор v1 == 45 && v2 == 45, но я его не вижу. Любая идея ? - person barjak; 12.09.2010
comment
Будет ли v1 == v2 == 45 иметь смысл? Я почти не умею читать на Scala, поэтому не знаю, как это туда втиснуть. - person Esko; 19.09.2010
comment
@Esko в Scala нет сложных условий, это не сработает. Я думал об арифметическом трюке или операции с битами, но не вижу ни одной. - person barjak; 20.09.2010
comment
v1*256 + v2 == ... или даже v1<<8 | v2 == ... - person Nakilon; 20.09.2010

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 = """+-+ +--+  +--+
| | |  |  |  |
+-+ |  |  + -+
    |  |      
    +--+  +-+ 
  +--+    |   
  +--+    +-+ """

or:

>>>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) верх 1-го поля (30,33) низ того же поля, (4,8) верх 2-го поля и так далее.

person Community    schedule 19.01.2011
comment
Есть ли что-то странное с 2.6 (или вашей установкой python)? Получаю пустой список на 2.5 и 2.7. - person David X; 21.01.2011
comment
@DavidX: Ничего странного, ты прав. Я думал, что это потому, что я вставил новую строку в начало входной строки, но все же исправил, что что-то пошло не так. Теперь я (повторно) скопировал все это из своего источника, и, похоже, все работает нормально. - person MattiaG; 21.01.2011

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]<-' '

Ищите плюс. Найдите тот, который находится справа от него. Найдите тот, что ниже. Распечатайте информацию этого прямоугольника и обнуляйте плюсы, которые мы уже использовали. Поскольку каждый плюс - это только часть одного допустимого прямоугольника, это все, что нам нужно сделать.

person Community    schedule 10.09.2010
comment
Это приведет к выводу недопустимых прямоугольников, где, скажем, - или | символов нет или нет левой или нижней стороны. - person Andrew Cooper; 10.09.2010
comment
Спецификация запрещает это. Он говорит, что "+" появляется только как часть единственного допустимого прямоугольника. Похоже, это означает, что к недопустимым / неполным прямоугольникам не прикреплены символы «+». - person Brian; 10.09.2010
comment
В моем понимании это предложение означает, что не существует 2-го ДЕЙСТВИТЕЛЬНОГО прямоугольника, который включает тот же +. Это не говорит о том, что нет других появлений (как часть недопустимого прямоугольника, как часть действительного треугольника, автономно и т. Д.) - person Nas Banov; 10.09.2010
comment
@Nas Banov, это то, что я имел в виду, но понять это явно труднее, чем я ожидал. - person David X; 10.09.2010

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,'')

Вы можете запустить это (с помощью XQSharp), установив переменную $i как строки ввода

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

2 0 3 2  0 1 3 3

Я полагаю, можно было бы возразить, что declare variable $i external; просто настраивает ввод и поэтому не добавляет к счету, и в этом случае 275 символов

person Community    schedule 13.09.2010
comment
Это действительно хороший тестовый пример. Надеюсь, вы не возражаете, что я украду его для теста в моем ответе. - person ninjalj; 20.09.2010