虚空传送门检测


53

电子游戏Minecraft就是要在构成虚拟世界的3D 整数格子中放置和移除不同类型的块。每个晶格点可以只包含一个块,也可以是空的(正式为“ air ”块)。在此挑战中,我们将只关注3D世界的一个垂直2D平面和一种块:obsidian

当黑曜石在垂直平面上形成一个空矩形的轮廓时,可以创建一个下界门户。空矩形的大小可以从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 x 3,完全为空,以黑曜石为边界。因此,它是有效的。
  • 门户3并非完全为空。因此无效。
  • 传送门4为3 x 3,完全为空,以黑曜石为边界。因此,它是有效的。
  • 传送门5为3 x 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 2015年

@Zgarb不,我仍然希望输入看起来像网格。
加尔文的兴趣爱好

4
门户网站的大小何时从静态2x3更改为可选的较大尺寸?
Sparr 2015年

5
@Sparr SInce 1.7.2(请参阅更新历史记录)。我不确定他们是否可以在控制台版本中执行此操作。
加尔文的业余爱好

4
因为我的世界肯定是+1。
Alex A.

Answers:


24

Perl,81 86

使用多个正则表达式。

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

门户的特定宽度的regexp比通用的更简单:X{$m}..{$n}(X\.{$m}X.{$n}){3,22}.X{$m}其中m是门户的宽度,ntotal width - 1 - m(?=...)由于匹配项可能重叠,因此必须将正则表达式放入零宽度的前向断言中。然后,我对该正则表达式设置$n和进行21次迭代$."@-"计算到最后一个匹配(/.\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)

您可以在RegexHeroRegexStorm上对其进行实时测试。匹配项将是门户网站上最黑曜石的行。如果您发现一个失败的测试用例,请告诉我!

这是什么法术?

以下说明假定您对.NET的平衡组有基本的了解。要点是捕获是.NET正则表达式中的堆栈-每个具有相同名称的新捕获都被推送到堆栈上,但是还存在从这些堆栈中再次弹出捕获的语法,以及从一个堆栈中弹出捕获并推送捕获的语法同时移到另一个。要获得更完整的图片,您可以查看我对Stack Overflow的回答,其中应涵盖所有细节。

基本思想是匹配以下模式:

 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之间(含)。棘手的事情是使所有ns和所有ms都相同。由于实际字符将不同,因此我们不能仅使用反向引用。

请注意,正则表达式必须嵌入换行符,\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 2015年

@ user23013哦,很好,谢谢。
马丁·恩德


@ user23013我使用未命名的组获得了114 ,但是没有将行检查合并为一个。
马丁·恩德

11

Python,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 2015年

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;
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.