テトリス!最終的な高さ(3日目)


19

大学のコードチャレンジコンテストからのチャレンジ

これは実際には0日目ですが、昨日の挑戦はあまりにも簡単で、ここで別の質問をだましてしまう可能性があります。


テトリスは、80年代に人気を博したビデオゲームです。ボードに収まるさまざまな形状の一連のピースを配置して、可能な限りコンパクトに収まるようにします。

この問題では、それぞれが特定の位置にあり、変更できない特定の向きで落ちる一連のピースを想定します。ピースは落下するにつれて積み上げられ、完全な行は削除されません(元のゲームのように)。目的は、すべての破片が落ちた後、ボードの各列の最終的な高さを決定することです。

図に示すように、合計7つの異なる部分があります。

形

チャレンジ

ピースのリストが与えられたら、すべてのピースが落ちた後、ボードからすべての列の高さを出力します

ピースは、I、R、Pの3つの数字で構成されます。最初の数字Iは、ピースの識別子です(図と同じ順序で1〜7の数字)。2番目の数値Rは、ピースの回転です。0、90、180、または270の値を取ることができ、反時計回り方向のピースの回転角度を表します。3番目の数字Pは、ピースの位置を示します。ピースが占める左側の列を表します(これは1または0のインデックスになります。指定してください)。

例とテストケース(1インデックス)

  • 与えられた [[1, 0, 1], [4, 0, 1], [5, 90, 4]]

ケース#1

  • 出力 [3, 3, 1, 3, 2]

  • 与えられた [[6, 270, 4], [1, 180, 5], [1, 90, 6], [7, 0, 4]]

ケース#2

  • 出力 [0, 0, 0, 9, 9, 8, 3, 3]

  • 与えられた[[3,0,1],[3,180,3]]出力[1,1,4,4,4]

  • 与えられた[[2,180,1],[2,0,3]]出力[2,2,4,3,3]

ノート

  • これは
  • 行/列には1または0のインデックスを指定できます。ご指定ください。
  • 入力値を再定義できます(おそらく、ピース1をAとして呼び出すなど)。その場合は指定してください

ご質問

  • 度単位の角度の代わりに、4つの異なる値を使用できますか?:はい

  • ピースが前のピースに正確に合わない場合、「穴」を処理することになっていますか?:はい

  • ボードの高さまたは幅は制限されていますか?いいえ。幅も高さも制限されていません


画像とテストケースについて@Arnauldに感謝します*。*


ことができIRそしてP異なる順序で入力することが?
ニール

@ニールはい。順序は
自由です

入力値を再定義できる場合、ピースの形状を(回転なしで)表現する行列としてピースIDを取得できますか?
無知の具現化

1
2つの理由から、ピースの形状を表すマトリックスを入力できないと思います。入力は明確に定義されます:1,2,3 ..またはA、B、C.。そして、この課題の基本的な部分はこの制約を管理することです。
アステカ

1
末尾の0を含めても大丈夫でしょうか?
ダナ

回答:


10

JavaScript(Node.js) 286 284 270  266バイト

[0..3]

a=>a.map(([p,r,x])=>(g=y=>y>3?g(+!Y--):b[Y+y]&(m[y]=('0x'+`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`[(p*2+r*56+y*99+13)%113])<<x)?m.map(v=>(g=x=>v&&g(x+1,H[x]=v&1?Y:~~H[x],v>>=1))(0,b[++Y]|=v)):g(y+1))(Y=a.length*4),m=[b=[-1]],H=[])&&H

オンラインでお試しください! または、最終的なボードも表示する拡張バージョン試してください

形状エンコード

すべてのピースは正確に4ニブル(4x4ビット)として格納され、行は逆順でソートされ、左端のピクセルは最下位ビットにマップされます。つまり、形状のバイナリ表現は、垂直と水平の両方にミラーリングされます。

例:

形状エンコードの例

ハッシュ関数とルックアップテーブル

p[0..6]r[0..3]y[0..3]n

n=(2p+56r+99y+13)mod113

最初のエントリのみが明示的に保存されます。それ以外はすべて設定され。820

これらのエントリは次のようにパックされています。

`717433667233ff4717333327661${1e12+0x5e7056a566ffff57efa65n.toString(4)}`

次の82ニブルに展開されます。

"717433667233ff47173333276611000000000000113213001112221112123333333311133233221211"

最終フォーマットで16進数を使用する必要があるのは、ピースの2つの水平表現にのみ必要です。したがって、上記のストリングにあります。I"ff"

ハッシュ関数のパラメーターは、先行ゼロと後続ゼロを最適化する方法でブルートフォースされました。1e12中央のゼロを使用して文字列をさらに圧縮し、右側の部分をベース16からベース4に変換することは、歓迎されますが予期しない副作用です。:-)

これは、すべてのピースとすべてのローテーションの展開プロセスのデモンストレーションです。

コメント済み

a => a.map(([p, r, x]) => (     // for each piece p with rotation r and position x:
  g = y =>                      //   g = recursive function taking y
    y > 3 ?                     //   if y is greater than 3:
      g(+!Y--)                  //     reset y to 0, decrement Y and try again
    :                           //   else:
      b[Y + y] & (              //     test if we have a collision of the board with
        m[y] =                  //     the y-th row m[y] of the current piece
          ('0x' + `717...`[     //     which is extracted from a lookup table
            (p * 2 + r * 56 +   //     using the hash function described in the
             y * 99 + 13) % 113 //     previous paragraph
          ]) << x               //     and shifted to the left according to x
      ) ?                       //     if we have a collision:
        m.map(v => (            //       we iterate again on the piece rows stored in m[]
          g = x =>              //         g = recursive function taking x
            v &&                //         if v is not equal to 0:
            g(                  //           do a recursive call:
              x + 1,            //             increment x
              H[x] =            //             update the height at x:
                v & 1 ?         //               if this bit is set:
                  Y             //                 set it to Y
                :               //               else:
                  ~~H[x],       //                 leave it unchanged or force it to 0
                                //                 if it was still undefined
              v >>= 1           //             shift v to the right
            )                   //           end of recursive call
          )(0,                  //         initial call to g with x = 0
               b[++Y] |= v)     //         increment Y and copy the piece row to the board
        )                       //     end of map()
      :                         //   else (no collision):
        g(y + 1)                //     do a recursive call to test the next row
  )(Y = a.length * 4),          //   initial call to g with y = Y = 4 * the number of pieces
                                //   (assuming the worst case: piled vertical I pieces)
  m = [b = [-1]], H = []        //   initialize m[], b[] and H[]
                                //   we set a full line at the bottom of b[]
) && H                          // end of map(); return H[]

3
ピースを梱包/開梱するのは素晴らしい仕事です。それは本当に印象的です:)
ダナ

7

C(clang)253 239 221 212バイト

t(*a,*b,c){char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVWhU😎EQV😀RTYT😉UU";for(size_t*p,f,n,y,i;c--;b++){f=1<<(8-*b)/3;p=z+*b++*8+*b++%f*2;f=n=*p;for(y=i=0;i<=f%4;y=fmax(y,a[*b+i++]+n%4))n/=4;for(;i--;a[*b+i]=y+n%4)n/=4;}}

オンラインでお試しください!

ps実際には、UTF-8でコード化されたUNICODE文字のため、コードサイズは221バイト(ただし212文字)です。しかし、tio.runはそれを212バイトのコードとして扱います...

コンピューターのコードサイズは209文字(218バイト)です。しかし、私はtio.runで\225目に見えるcharに置き換えることができませんでした 😞

未ゴルフコード

// a - output array (must be zeroed), b - array of block info, c - number of blocks

// Figure codes: 2->0, 3->1, 6->2, 1->3, 5->4, 7->5, 4->6 (0,1 are L-figures, 2 is is T-figure, 3 is a line 1x4; 4,5 are zigzags; 6 is a cube 2x2)
// Vertical and horizontal positions are zero-indexed, angles = 0..3

t(*a,*b,c)
{
  char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVWhU😎EQV😀RTYT😉UU";  // UTF-8
//char*z="VP\225TBUIUVAaUZ@AWVDeTf@EVW\1hU😎\26EQV😀RTYT😉UU";  // 3 bytes longer (use it if you can't copy previous string correctly) :)
  // Blocks
  for(size_t*p,f,n,y,i;c--;b++){
    f=1<<(8-*b)/3;  // number of figure variants
    p=z+*b++*8+*b++%f*2;
    // Get top base line position (y)
    f=n=*p;  // figure width, TBLs and HATs
    for(y=i=0;i<=f%4;
      y=fmax(y,a[*b+i++]+n%4))
      n/=4;
    // Add heights (HATs)
    for(;i--;
      a[*b+i]=y+n%4)
      n/=4;
  }
}  // 215 chars (224 bytes)

説明

各図の一番上のベースライン(TBL)を見つけて、それを各水平位置のTBLの下のセルの数として説明しましょう。また、TBL(HAT)上のセルの数(高さ)を説明しましょう。

例えば:

                       ________ ________
_ [] _____ HAT = 1,0,0 [] [] [] HAT = 0,0,0 ___ [] [] _ HAT = 0,1,1 [] [] [] HAT = 0,0,0
 [] [] [] TBL = 1,1,1 [] TBL = 2,1,1 [] [] TBL = 1,1,0 [] TBL = 1,2,1

各図と各回転角度のTBLとHATを説明しましょう。

幅TBL HAT
----- ------- -------
L字:
  3 1 1 1 1 0 0 // 0°
  2 1 1 0 2 // 90°
  3 1 1 2 0 0 0 // 180°
  2 3 1 0 0 // 270°

  3 1 1 1 0 0 1 // 0°
  2 1 3 0 0 // 90°
  3 2 1 1 0 0 0 // 180°
  2 1 1 2 0 // 270°

T字形:
  3 1 1 1 0 1 0 // 0°
  2 1 2 0 1 // 90°
  3 1 2 1 0 0 0 // 180°
  2 2 1 1 0 // 270°

ライン:
  4 1 1 1 1 0 0 0 0 // 0°、180°
  1 4 0 // 90°、270°

ジグザグ:
  3 1 1 0 0 1 1 // 0°、180°
  2 1 2 1 0 // 90°、270°

  3 0 1 1 1 1 0 // 0°、180°
  2 2 1 0 1 // 90°、270°

キューブ:
  2 2 2 0 0 //任意の角度

(交換今は2ビットのシーケンスとしてこれらの番号を符号化し、アレイに入れなければならない4 0ことで3 1- ; 1と減少幅と同じになる結果の2ビットに収まるように、「ライン」の90°の角度のために)。

(2 LSB 単位)、TBLHAT(逆方向ループの逆方向)の順にエンコードします。たとえば2 2 1 1 0 、T字の角度が270°の場合、1 0 1 2 1(最後の1width-1)としてエンコードされます0b0100011001 = 281

12.02更新:

a)配列を文字列に変換して18文字を保存しました(以前の239バイトのコードを見ることができます):))

b)より最適化され、コードは9文字縮小されます。
これは私の最後の試みです(そうだと思います、笑!) 😀


1
を使用してストライクでき<s> ... </s>ます。
ジョナサンフレッチ


かっこいい、イケてる。ありがとう。笑:))
ジンX

うわー!低レベルテトリス🤔
Rustem B.

TBLは、最上部のラインの下にある数字セルの数であり、その下と上に空きスペースまたはセルブロックのみがあります(空きスペースとセルはありません)。TBL + HAT =図の高さ(各水平位置)。TBL> 0およびHAT> 0も。
ジンX

5

Common Lisp、634バイト

(let((w(make-hash-table))(r 0))(defun z(c)(or(gethash c w)0))(defun x(c v)(setf r(max r c))(setf(gethash c w)v))(defun m(s)(dolist(c s)(apply(lambda(n u p)(let*((i(let*((j'(2 2 2))(k'(3 3))(l'(2 3))(m'(3 2))(o(case n(1(list'(1 1 1 1)'(4)))(2(list j k'(1 1 2)'(3 1)))(3(list j'(1 3)'(2 1 1)k))(4(list'(2 2)))(5(list'(2 2 1)l))(6(list j l'(1 2 1)m))(7(list'(1 2 2)m)))))(setf(cdr(last o))o)))(o(nth(+ u 2)i))(b(nth u i))(s(length o))(d 0)(h 0))(dotimes(i s)(let*((w(nth i b))(g(z(+ i p)))(m(+ g w)))(when(> m d)(setf d m)(setf h(- g(-(apply'max b)w))))))(dotimes(i s)(x(-(+ s p)i 1)(+(nth i o)h)))))c))(dotimes(i r)(print(z (+ i 1))))))

冗長

(defun circular (list)
  (setf (cdr (last list)) list))

(defun get-piece (piece-number)
  (circular (case piece-number
              (1 (list '(1 1 1 1)
                       '(4)))
              (2 (list '(2 2 2)
                       '(3 3)
                       '(1 1 2)
                       '(3 1)))
              (3 (list '(2 2 2)
                       '(1 3)
                       '(2 1 1)
                       '(3 3)))
              (4 (list '(2 2)))
              (5 (list '(2 2 1)
                       '(2 3)))
              (6 (list '(2 2 2)
                       '(2 3)
                       '(1 2 1)
                       '(3 2)))
              (7 (list '(1 2 2)
                       '(3 2))))))

(let ((world (make-hash-table))
      (rightmost-column 0))
  (defun get-world-column (column)
    (or (gethash column world) 0))

  (defun set-world-column (column value)
    (setf rightmost-column (max rightmost-column column))
    (setf (gethash column world) value))

  (defun drop-piece (piece-number rotation position)
    (let* ((piece (get-piece piece-number))
           (top (nth (+ rotation 2) piece))
           (bottom (nth rotation piece))
           (size (length top))
           (max-combined-height 0)
           (contact-height 0))
      (dotimes (i size)
        (let* ((down-distance (nth i bottom))
               (height (get-world-column (+ i position)))
               (combined-height (+ height down-distance)))
          (when (> combined-height max-combined-height)
            (setf max-combined-height combined-height)
            (setf contact-height
                  (- height
                     (- (apply #'max bottom)
                        down-distance))))))
      (dotimes (i size)
        (set-world-column (- (+ size position) i 1)
                          (+ (nth i top) contact-height)))))

  (defun drop-pieces (pieces)
    (dolist (piece pieces)
      (apply #'drop-piece piece)))

  (defun print-world ()
    (loop for i from 1 to rightmost-column
          do (print (get-world-column i)))))

(defun play-tetris (pieces)
  (drop-pieces pieces)
  (print-world))

試して

ピースは数字のリストの循環リストです。これらのサブリストはそれぞれ形状の側面を表し、数字は反対側からの距離を示します。それらは、その側が下にある場合は左から右、上にある場合は右から左、左にある場合は上から下、右にある場合は下から上です。これらの設計選択により、ローテーション用のコードを記述する必要がなくなります。残念ながら、回転コードの欠如は、長い形状表現、または新しい列の高さを計算するために使用したやや複雑なロジックを補っていなかったようです。

回転は負でない整数です。0 = 0度、1 = 90度、2 = 180度、4 = 270度


5

C#(Visual C#Interactive Compiler)、308バイト

a=>{var o=new int[a.Max(x=>x.Item3+4)];foreach(var(i,r,p)in a){var b="\"4TqzŒª!\0\0HSš	Ó\0$\n\0!“A“š š@";int m=0,n=b[i],t=0,u=n/8+r%(n%8),v=b[u*=2]<<8|b[u-1];for(;t<v/8%8;m=m>n?m:n)n=o[p+t]+v%8-(n=(u=v>>6+3*t++)/2&1)-(n&u);for(;t-->0;)o[p+t]=m-(n=(u=v>>6+3*t)/4&1)-(n&u);}return o;}

オンラインでお試しください!

OK-それは狂気だった...すぐに使えるコードゴルフのテクニックを使った答えを提出した。しかし、他の人が提出しているものを見たとき、より良い方法があることに気づきました。

(shape, rotation)タプルは、重複が削除されたC#文字列リテラルにエンコードされます。エンコードプロセスは、これらの各構成を2バイトでキャプチャします。

下位3ビットは高さを格納し、次の3ビットは幅を格納します。これらの値はそれぞれ4を超えることはないため、変換せずに3ビットから直接読み取ることができます。ここではいくつかの例を示します。

  W   H
010 010 (2x2)
010 011 (2x3)
001 100 (1x4)
011 010 (3x2)
100 001 (4x1)

次に、各列は3ビットで保存されます。私が保存するのに最も便利なのは、列の上部と下部から欠落している正方形の数です。

// missing squares per column

+------ 0 top / 0 bottom
|+----- 0 top / 1 bottom
||+---- 0 top / 1 bottom
|||
HHH (L-Shape)         HH (Jagged-Shape)
H                    HH
                     |||
1 top / 0 bottom ----+||
0 top / 0 bottom -----+|
0 top / 1 bottom ------+

上または下から2マス以上が欠落することはなく、同時に両方から1マス以上が欠落することはありません。この一連の制約を考えると、次のエンコーディングを思いつきました。

// column encoding of missing squares per column

000: none missing
100: 1 missing on top
101: 2 missing on top
010: 1 missing on bottom
011: 2 missing on bottom
110: 1 missing on top and bottom

上下に欠落している正方形の最大3列を考慮する必要があるため、各(shape, rotation)タプルを15ビットでエンコードできます。

 C3  C2  C1   W   H
000 000 000 010 010 - 2x2 with no missing squares
000 000 000 100 001 - 4x1 with no missing squares
100 000 100 011 010 - 3x2 with missings square on top of columns 1 and 3
000 110 000 010 011 - 2x3 with missing squares on top and bottom of column 2

最後に、重複する形状が削除されました。次の例は、複数の(shape,rotation)タプルが異なる回転で同じ形状の重複出力を生成する方法を示しています。

// Square
HH  (0, 90, 180, 270)
HH
#-------------------------------#
// ZigZag
HH  (0, 180)    H  (90, 270)
 HH            HH
               H
#-------------------------------#
// T
 H  (0)        HHH  (180)
HHH             H

 H  (90)       H    (270)
HH             HH
 H             H

すべての一意の出力が決定され、に保存byte[]され、C#文字列リテラルに変換されます。すばやく形状をに基づいているルックアップするためにI、およびR、配列の最初の7つのバイトは、符号化された検索キーから成ります。

以下は、断片を圧縮するために使用したプログラムへのリンクです。

オンラインでお試しください!

少ないゴルフとコメントのコード:

// a: input list of (i,r,p) tuples
a=>{
  // create an output array that 4 more than
  // the largest position. this may result
  // in some trailing 0's
  var o=new int[a.Max(x=>x.Item3+4)];

  // iterate over each (i,r,p) tuple
  foreach(var(i,r,p)in a){
    // escaped string
    var b="\"4Tqzª!\0\0HS   Ó\0$\n\0!A @";
    // declare several variables that will be used later
    int m=0,n=b[i],t=0,
      // u is the decoded index into b for the current (i,r) pair
      u=n/8+r%(n%8),
      // convert 2 bytes from b into an encoded (shape,rotation) pair
      v=b[u*=2]<<8|b[u-1];
    // iterate over the columns, determining the top of the current
    // piece. The formula here is:
    //   piece_top = max(column_height + shape_height - shape_space_bottom)
    for(;t<v/8%8;m=m>n?m:n)
      n=o[p+t]+v%8-(n=(u=v>>6+3*t++)/2&1)-(n&u);
    // iterate over the columns again, saving the the new height
    // in each column. The formula here is:
    //   new_column_height = piece_top - shape_space_top
    for(;t-->0;)
      o[p+t]=m-(n=(u=v>>6+3*t)/4&1)-(n&u);
  }
  return o;
}

4

チャコール、98バイト

Fθ«≔§⪪§⪪”)¶∧↷"e«↨U∧0%3;D∧⁼~h⊟⁵h/₂dΦ↗(EF”2⊟ι1⊟ιη≔⊟ιζW‹Lυ⁺ζLη⊞υ⁰≔⌈Eη⁻§υ⁺ζλ﹪Iκ³εFLη§≔υ⁺ζκ⁺ε⊕÷I§ηκ³»Iυ

オンラインでお試しください!リンクは、コードの詳細バージョンです。入力を[P、R、I]値の配列として受け取ります。ここで、Iは0〜6で、Rは0 o 3で、Pも0でインデックス付けされています。説明:

Fθ«

入力ピースをループします。

≔§⪪§⪪”)¶∧↷"e«↨U∧0%3;D∧⁼~h⊟⁵h/₂dΦ↗(EF”2⊟ι1⊟ιη

現在のピースと回転の説明を抽出します。(下記参照。)

≔⊟ιζ

位置を抽出します。

W‹Lυ⁺ζLη⊞υ⁰

ピースを置くのに十分な水平スペースがあることを確認します。

≔⌈Eη⁻§υ⁺ζλ﹪Iκ³ε

ピースを配置するのに十分な垂直スペースがあることを確認します。

FLη§≔υ⁺ζκ⁺ε⊕÷I§ηκ³

影響を受ける列の新しい高さを計算します。

»Iυ

すべてのピースが処理されたら、列の高さの最終リストを個別の行に出力します。

圧縮された文字列は、元の文字列を表します00001923001061443168200318613441602332034173203014614341642430137。ここで、2sはIセパレーターであり、1sはRセパレーターです。したがって、ピースは次のようにデコードされます。

P\R  0    1    2    3
0    0000 9
1    300  06   443  68
2    003  86   344  60
4    33
5    034  73
6    030  46   434  64
7    430  37

欠損R値は、木炭によって周期的に自動的に埋められます。次に、次の表に従って、各数字を2つの値、オーバーハングと全高にマッピングします。

\ O H
0 0 1
3 0 2
4 1 2
6 0 3
7 1 3
8 2 3
9 0 4

オーバーハングと全体の高さは、次のように列の高さに関係します。特定の位置eに配置するピースがある場合、列の1つがの高さよりも高い場合でもピースを配置できる場合がありますe。予備スペースの量は、オーバーハングによって与えられます。ピースを配置した後の列の新しい高さは、単に配置された位置と合計の高さです。

例:私たちは配置することによって、開始と仮定5片はしたがって、位置0と列1および3に配置された他に何もまだありませんので、列1の部分は、今私たちは、その後、配置したいコラム2は、高さ2を有している、高さ1を持つ6作品を1列0 を回転させます。ここでは、実際にこのピースを位置0に配置できます。列1の高さは1ですが、ピースのオーバーハングは1であるため、配置するのに十分なスペースがあります。列0の高さは2になり、列1の高さは3になります。

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