ネザーポータルの検出


53

ビデオゲームMinecraftは、仮想世界を構成する3D 整数ラティスにさまざまな種類のブロックを配置および削除することをすべて目的としています。各格子点には、ブロックを1つだけ含めるか、空にすることができます(公式には「空気」ブロック)。この課題では、3Dワールドの1つの垂直2Dプレーンと、1つのタイプのブロック(黒曜石)のみを考慮します。

黒曜石が垂直面で空の長方形の輪郭を形成するとき、ネザーポータルを作成できます。空の長方形は、幅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 x 3ユニットで、完全に空で、黒曜石で囲まれています。したがって、それは有効です。
  • ポータル2は4 x 3で、完全に空で、黒曜石で囲まれています。したがって、それは有効です。
  • ポータル3は完全に空ではありません。したがって、それは無効です。
  • ポータル4は3 x 3で、完全に空で、黒曜石で囲まれています。したがって、それは有効です。
  • ポータル5は3 x 2ユニットで、小さすぎます。したがって、それは無効です。
  • ポータル6には境界線の一部がありません。したがって、それは無効です。

チャレンジ

黒曜石と空のグリッドのこれらの文字列表現を取り込んで、存在する有効なネザーポータルの数を出力するプログラムまたは関数を作成します。

  • 入力は、stdin、ファイル、または関数の引数から取得できます。
  • 、テキストのすなわち完全な矩形グリッドを少なくとも1ワイド文字と背の高い、のみ含む-あなたは、入力が常にうまく形成されていると仮定するX.。オプションで、最後の行の後に末尾の改行があると仮定できます。

  • 必要であれば、あなたは、任意の2つの異なる使用することができ、印刷可能な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。
アレックスA.

回答:


24

Perl、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(?=...)一致は重複する可能性があるため、正規表現はゼロ幅の前方アサーションに配置する必要があります。次に、この正規表現の設定$nとを21回繰り返し$.ます。"@-"最後のmatch(/.\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正規表現のスタックであるということです-同じ名前の新しいキャプチャはすべてスタックにプッシュされますが、それらのスタックからキャプチャを再度ポップする構文と、1つのスタックからキャプチャをポップしてキャプチャをプッシュする構文もあります同時に別のものに。より完全な図については、すべての詳細をカバーする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
うーん、純粋な正規表現の答えについてどう思うかわかりません。トップを一致させることは、数字を印刷することと同じではありません。もちろん、プログラムで正規表現を使用し、一致する数を出力することは問題ありません。それでも、あなたが言うように、おそらくbeatられますので、私も心配です。
カルバンの趣味

1
^(または未使用の文字)を使用できます(?!)
jimmy23013

@ user23013ああ、良い点、ありがとう。
マーティンエンダー


@ user23013 名前のないグループを使用しただけで 114になりましたが、ラインチェックを1つに結合していません
マーティンエンダー

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よりも優れていますが、5重のネストされたループが痛いです。は、置換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のネストされたループ生成ソーサリーを試すことです。
イマレット

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

}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.