Виявлення порталу


53

Відеогра Minecraft це все про розміщення і видалення різних типів блоків в 3D целочисленной решітці , що становить віртуальний світ. Кожна точка решітки може містити рівно один блок або бути порожньою (" повітряний " блок офіційно). У цьому виклику ми будемо стосуватися лише однієї вертикальної двовимірної площини 3D-світу та одного типу блоку: обсидіану .

Коли обсидіан утворює контур порожнього прямокутника у вертикальній площині, може бути створений нижній портал . Порожній прямокутник може бути будь-якого розміру від 2 одиниць завширшки до 3 одиниць висоти до 22 одиниць завширшки до 22 одиниць заввишки. Кути прямокутника не потрібно межувати з обсидіаном, лише сторони.

Наприклад, припустимо, що Xце обсидіан і .є порожнеча: (Цифри призначені лише для ідентифікації, а також порожні.)

...................................
..XXXX....XXXX....XXXXXXXXX........
..X..X...X....X..X.........X..XXXX.
..X.1X...X.2..X..X...3...X.X..X....
..X..X...X....XXXX.........X..X.6X.
..XXXX....XXXX...XXXXXXXXXXX..X..X.
.............X.4.X....X.5.X...XXXX.
.............X...X....X...X........
..............XXX......XXX.........
...................................

Ця сітка містить 3 дійсних портали:

  • Портал 1 - це 2 на 3 одиниці, повністю порожній і межує з обсидіаном. Тому це дійсно.
  • Портал 2 - 4 на 3, повністю порожній і межує з обсидіаном. Тому це дійсно.
  • Портал 3 не зовсім порожній. Тому він недійсний.
  • Портал 4 - 3 на 3, повністю порожній і межує з обсидіаном. Тому це дійсно.
  • Портал 5 - це 3 на 2 одиниці, що занадто мало. Тому він недійсний.
  • На порталі 6 відсутня частина кордону. Тому він недійсний.

Виклик

Напишіть програму або функцію, яка містить у цих рядкових поданнях сітки обсидіану та порожнечі та друкує або повертає кількість присутніх дійсних порталів.

  • Вхід може бути з аргументу stdin, файлу або функції.
  • Ви можете припустити, що введення завжди добре сформоване - тобто ідеально прямокутна сітка тексту, щонайменше 1 символ у ширину та висоту, містить лише Xта .. Ви можете припустити, що після останнього рядка є останній рядок.

  • За бажанням ви можете використовувати будь-які два відмінних символи для друку ASCII замість Xта ..

  • Обсидіан може бути на межах сітки. Все, що виходить за межі, вважається порожнім.

Приклад введення - вихід повинен бути 4:

................................................................
...................................XXXXXXXXXXXXXXXXXXXXXXXXX....
..XXXX....XXXX....XXXXXXXXX........X.......................X....
..X..X...X....X..X.........X..XXXX.X.......................X....
..X..X...X....X..X.......X.X..X....X.......................X....
..X..X...X....XXXX.........X..X..X..XXXXXXXXXXXXXXXXXXXXXXXX....
..XXXX....XXXX...XXXXXXXXXXX..X..X.X......................X..XXX
.............X...X....X...X...XXXX.X......................X..X..
.............X...X....X...X........X......................X..X..
..............XXX......XXX........XXXXXXXXXXXXXXXXXXXXXXXX...X..
..................................XX.........................XXX

Оцінка балів

Виграє подання з найменшими байтами .


Чи можна використовувати інший символ ASCII замість нових рядків?
Згарб

@ Zgarb Ні, я все ще хочу, щоб вхід виглядав як сітка.
Захоплення Кальвіна

4
Коли розмір нижчих порталів змінився зі статичного 2x3 на необов'язкові великі розміри?
Спарр

5
@Sparr SInce 1.7.2 (див. Історію оновлень ). Я не впевнений, чи зможуть вони це зробити у консольних виданнях.
Захоплення Кальвіна

4
Однозначно +1, тому що Minecraft.
Олексій А.

Відповіді:


24

Перл, 81 86

Використання декількох регулярних виразів.

#!perl -p0
$_=map{/.
/;$n="@-"-++$.;/(?=X{$.}..{$n}(X\.{$.}X.{$n}){3,22}.X{$.})/gs}($_)x21

Зображення для конкретної ширини порталу набагато простіше, ніж загальне: X{$m}..{$n}(X\.{$m}X.{$n}){3,22}.X{$m}де mширина порталу і nяка total width - 1 - m. Повторне вираження повинно бути поставлене в нульовій ширині вперед, (?=...)оскільки збіги можуть перекриватися. Потім я повторюю 21 раз над цим параметром регулярного вибору $nі $.. "@-"оцінює для початкової позиції останнього збігу ( /.\n/), яка має загальну ширину - 1. $.використовується як інша змінна, оскільки вона ініціалізована 1при використанні з -p0.


2
Ви можете зберегти байт, якщо ви використовуєте інший символ, ніж .для порожніх комірок (тому вам не доведеться уникати його).
Мартін Ендер

62

Регекс (аромат .NET), 182 181 145 132 126 114 104 100 98 97 96 байт

2D розпізнавання образів ASCII мистецтва? Звучить робота для регексу! (Це не так.)

Я знаю, що це знову спричинить нескінченні дискусії про те, чи є подання регулярних програм дійсними програмами, чи ні, але я сумніваюся, що це все одно переможе APL чи CJam, тому я не бачу ніякої шкоди. (Це , як то кажуть, вони роблять пройти наш твердолобий тест на «Що таке мова програмування?» .)

Це сприймає дані як рядок, який повинен відповідати, а результат - кількість знайдених відповідностей. Він використовується _замість ., тому що мені доведеться уникати останнього. Він також вимагає зворотного нового рядка.

(X(X){1,21})(?=\D+((?>(?<-2>_)+)_))(?=.((?!\7)(.)*
.*(X\3X|()\1.)(?=(?<-5>.)*(?(5)!)
)){4,23}\7)

Ви можете перевірити його наживо на RegexHero або RegexStorm ). Матчі будуть верхніми обсидіанськими рядами порталів. Якщо ви зможете знайти тестовий випадок, де він не вдається, повідомте мене про це!

Що це за чаклунство?

Наступне пояснення передбачає базове розуміння балансуючих груп .NET . Суть полягає в тому, що захоплення - це стеки в регулярному виразі .NET - кожне нове захоплення для одного і того ж імені висувається на стек, але також є синтаксис, щоб знову вивести захоплення з цих стеків, а також синтаксис для поп-захоплення з однієї стеки та push-захоплення. на інший одночасно. Для більш повної картини ви можете подивитися мою відповідь на "Переповнення стека", яка повинна охоплювати всі деталі.

Основна ідея - відповідати такому малюнку, як:

 X{n}..{m}
X_{n}X.{m} |
X_{n}X.{m} |  3 to 22 times
X_{n}X.{m} |
 X{n}..{m} 

Де nзнаходиться між 2 та 22 (включно). Хитра річ - це все, щоб усі nі всі mбули однаковими. Оскільки фактичні символи не будуть однаковими, ми не можемо просто використовувати зворотну референцію.

Зауважте, що регулярний вираз повинен вбудовувати нові рядки, про які я напишу, як \nу наступному.

(                     # Open capturing group 1. This will contain the top of a portal, which
                      # I can reuse later to match the bottom (being of the same length).
  X                   # Match a single X.
  (X){1,21}           # Match 1 to 21 X's, and push each separately on the <2> stack. Let's
                      # Call the number of X's captured N-1 (so N is the inner width of the
                      # portal).
)                     # End of group 1. This now contains N X's.
(?=                   # Start a lookahead. The purpose of this lookahead is to capture a 
                      # string of N underscores in group 2, so I can easily use this to match 
                      # the inside rows of the portal later on. I can be sure that such a 
                      # string can always be found for a valid portal (since it cannot have 0 
                      # inner height).
  \D+                 # Skip past a bunch of non-digits - i.e. *any* of the vaild characters
                      # of the input (_, X, \n). This to make sure I search for my N 
                      # underscores anywhere in the remainder of the input.
  (                   # Open capturing group 3. This will contain a portal row.
    (?>               # This is an atomic group. Once the engine hass successfully matched the
                      # contents of this group, it will not go back into the group and try to
                      # backtrack other possible matches for the subpattern.
      (?<-2>_)+       # Match underscores while popping from the <2> stack. This will match as
                      # many underscores as possible (but not more than N-1).
    )                 # End of the atomic group. There are two possible reasons for the
                      # subpattern stopping to match: either the <2> stack is empty, and we've
                      # matched N-1 underscores; or we've run out of underscores, in which 
                      # case we don't know how many underscores we matched (which is not 
                      # good).
    _                 # We simply try to match one more underscore. This ensures that we 
                      # stopped because the <2> stack was empty and that group 3 will contain
                      # exactly N underscores.
  )                   # End of group 3.
)                     # End of the lookahead. We've got what we want in group 2 now, but the
                      # regex engine's "cursor" is still at the end of the portal's top.
(?=                   # Start another lookahead. This ensures that there's actually a valid
                      # portal beneath the top. In theory, this doesn't need to be a 
                      # lookahead - I could just match the entire portal (including the lines
                      # it covers). But matches cannot overlap, so if there were multiple
                      # portals next to each other, this wouldn't return all of them. By 
                      # putting the remainder of the check in a lookahead the actual matches
                      # won't overlap (because the top cannot be shared by two portals).
  .                   # Match either _ or X. This is the character above the portal side.

  (                   # This group (4) is where the real magic happens. It's purpose is to to
                      # count the length of the rest of the current line. Then find a portal
                      # row in the next line, and ensure that it's the same distance from the
                      # end of the line. Rinse and repeat. The tricky thing is that this is a
                      # single loop which matches both inner portal rows, as well as the 
                      # bottom, while making sure that the bottom pattern comes last.

    (?!\7)            # We didn't have a group 7 yet... group 7 is further down the pattern.
                      # It will capture an empty string once the bottom row has been matched.
                      # While the bottom row has not been matched, and nothing has been
                      # captured, the backreference will fail, so the negative lookahead will
                      # pass. But once we have found the bottom row, the backreference will
                      # always match (since it's just an empty string) and so the lookahead
                      # will fail. This means, we cannot repeat group 4 any more after the
                      # bottom has been matched.
    (.)*              # Match all characters until the end of the line, and push each onto
                      # stack <5>.
    \n                # Match a newline to go to the next line.
    .*                # Match as many characters as necessary to search for the next portal
                      # row. This conditions afterwards will ensure that this backtracks to
                      # the right position (if one exists).
    (                 # This group (6) will match either an inner portal row, or the bottom
                      # of the portal.
      X\3X            # Match X, then N underscores, then X - a valid inner portal row.
    |                 # OR
      ()              # Capture an empty string into group 7 to prevent matching further rows.
      \1.             # Use the captured top to match the bottom and another character.
    )
    (?=               # This lookahead makes sure that the row was found at the same 
                      # horizontal position as the top, by checking that the remaining line
                      # is the same length.
      (?<-5>.)*       # Match characters while popping from the <5> stack.
      (?(5)!)\n       # Make sure we've hit end of the line, *and* the <5> stack is empty.
    )
  ){4,23}             # Repeat this 4 to 23 times, to ensure an admissible portal height.
                      # Note that this is one more than the allowed inner height, to account
                      # for the bottom row.
  \7                  # Now in the above repetition there is nothing requiring that we have
                      # actually matched any bottom row - it just ensured we didn't continue
                      # if we had found one. This backreference takes care of that. If no
                      # bottom row was found, nothing was captured into group 7 and this
                      # backreference fails. Otherwise, this backreference contains an empty
                      # string which always matches.
)

C #, 185 байт

Ось повна функція C #, просто щоб зробити це дійсним записом. Пора я написав командний рядок "інтерпретатор" для регулярних виразів .NET ...

static int f(string p){return System.Text.RegularExpressions.Regex.Matches(p,@"(X(X){1,21})(?=\D+((?>(?<-2>_)+)_))(?=.((?!\7)(.)*
.*(X\3X|()\1.)(?=(?<-5>.)*(?(5)!)
)){4,23}\7)").Count;}

5
Хм, не впевнений, як я відчуваю чисту відповідь на регулярний вираз. Збіг верхів не є таким же, як друк номера. Звичайно, було б добре використовувати регулярну програму в програмі та надрукувати кількість збігів. Все-таки, як ви кажете, це, мабуть, буде побито, тому я теж переживаю.
Захоплення Кальвіна

1
Ви можете використовувати ^(або будь-який невикористаний символ) для (?!).
jimmy23013

@ user23013 О, хороший момент, дякую.
Мартін Ендер


@ user23013 Я отримав 114, використовуючи лише неназвану групу, але не поєднуючи рядкові чеки в одну.
Мартін Ендер

11

Пітон, 219 байт

def f(s):s=s.split();L=len;R=range;return L([r for r in R(L(s))for a in R(L(s[0]))for w in R(2,23)for h in R(3,min(L(s)+~r,23))if(s[r][a:a+w]==s[r-~h][a:a+w]==w*"X")*all(s[r-~k][a-1:a+w+1]=="X"+"."*w+"X"for k in R(h))])

Краще, ніж Java, але хлопчику боляче вкладаються петлі вкладені петлі. Це for/inможе бути трохи стисливим за допомогою %sзаміни, але це не заощадить багато.

Розширено:

def f(s):
  s=s.split()
  L=len
  R=range
  return L([r for r in R(L(s))
              for a in R(L(s[0]))
              for w in R(2,23)
              for h in R(3,min(L(s)+~r,23))
              if(s[r][a:a+w]==s[r-~h][a:a+w]==w*"X")* 
                 all(s[r-~k][a-1:a+w+1]=="X"+"."*w+"X"for k in R(h))])

1
Мій інстинкт полягає в тому, щоб спробувати чарівну генерацію циклу генерації циклу itertools.
imallett

7

Java, 304 байти

Це набагато довше, ніж звичайний вираз. Він просто перебирає кожен можливий квадрат у введенні. Якщо це дійсний портал, він збільшує лічильник на 1. Потім він повертає лічильник. Це, ймовірно, може бути гольф набагато далі. Будь-які пропозиції вітаються.

int a(String...a){a=a[0].split("\n");int b=0,c=0,d,e,f,g,h,i=a.length,j=a[0].length();for(;c<j;c++)for(d=0;d<i;d++)for(e=c+2;++e<j&e<c+24;)a:for(f=d+3;++f<i&f<d+24;){for(g=c;g<=e;g++)for(h=d;h<=f;h++){if(g==c|g==e&&h==d|h==f)continue;if((g==c|g==e|h==d|h==f)^a[h].charAt(g)>60)continue a;}b++;}return b;}

Відступ:

int a(String...a){
    a=a[0].split("\n");
    int b=0,c=0,d,e,f,g,h,i=a.length,j=a[0].length();
    for(;c<j;c++)
        for(d=0;d<i;d++)
            for(e=c+2;++e<j&e<c+24;)
                a:for(f=d+3;++f<i&f<d+24;){
                    for(g=c;g<=e;g++)
                        for(h=d;h<=f;h++){
                            if(g==c|g==e&&h==d|h==f)
                                continue;
                            if((g==c|g==e|h==d|h==f)^a[h].charAt(g)>60)
                                continue a;
                        }
                    b++;
                }
    return b;
}

Повна програма:

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class B {

    public static void main(String[] args) throws FileNotFoundException {
        String blocks = new BufferedReader(new FileReader(args[0])).lines().reduce((a,b)->a+"\n"+b).get();
        System.out.println(new B().a(blocks));
    }

    int a(String...a){
        a=a[0].split("\n");
        int b=0,c=0,d,e,f,g,h,i=a.length,j=a[0].length();
        for(;c<j;c++)
            for(d=0;d<i;d++)
                for(e=c+2;++e<j&e<c+24;)
                    a:for(f=d+3;++f<i&f<d+24;){
                        for(g=c;g<=e;g++)
                            for(h=d;h<=f;h++){
                                if(g==c|g==e&&h==d|h==f)
                                    continue;
                                if((g==c|g==e|h==d|h==f)^a[h].charAt(g)>60)
                                    continue a;
                            }
                        b++;
                    }
        return b;
    }

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.