Тетріс! Кінцеві висоти (3 день)


19

Виклик, взятий з мого конкурсу викликів університетського коду

Це насправді День 0, але вчорашній виклик був надто легким і тут може бути ще одним питанням.


Тетріс - це відеоігра, яка стала популярною у 80-х. Він полягає у розміщенні серії штук різної форми, які падають на дошку, щоб вони вмістилися максимально компактно.

У цій проблемі ми припустимо послідовність шматків, які падають, кожен у певному положенні та з певною орієнтацією, яку неможливо змінити. Шматки накопичуються в міру падіння і цілі ряди не усуваються (як у оригінальній грі). Мета - визначити кінцеву висоту кожного стовпчика дошки після падіння всіх шматочків.

На малюнку представлено 7 різних творів:

форми

Виклик

Враховуючи список штук, виведіть висоту всіх стовпців з дошки після падіння всіх шматочків

Елемент складається з трьох чисел: I, R і P. Перше число, I, є ідентифікатором шматка (число між 1 і 7, в тому ж порядку, як на рисунку). Друге число, R, - обертання деталі. Він може приймати значення 0, 90, 180 або 270 і являє собою кут повороту деталі в напрямку проти годинникової стрілки. Третє число, 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 за зображення та тестові приклади *. *


Може I, Rі Pвводити в іншому порядку?
Ніл

@Neil так. Це може бути в будь-якому порядку
Luis felipe De jesus Munoz

Якщо ми можемо переосмислити вхідні значення, чи можу я взяти ідентифікатор фрагмента як матрицю, що представляє форму шматка (без обертання)?
Втілення Невідомості

1
Я думаю, що ми не можемо ввести матрицю, що представляє фігуру форми з 2 причин. Вхід чітко визначений: 1,2,3 .. або A, B, C .. І однією з основних завдань цього завдання є управління цим обмеженням.
AZTECCO

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 грибки (4х4 біти), рядки відсортовані у зворотному порядку, а крайній лівий піксель, відображений на найменш значущий біт. Іншими словами, двійкове зображення форми відображається як вертикально, так і горизонтально.

Приклад:

приклад кодування фігури

Хеш-функція та таблиця пошуку

p[0..6]r[0..3]у[0..3]н

н=(2p+56r+99у+13)мод113

820

Ці записи упаковані у вигляді:

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

який розширюється до наступних 82 гризків:

"717433667233ff47173333276611000000000000113213001112221112123333333311133233221211"

Я"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
Приємна упаковка / розпакування штук. Це дійсно вражає :)
Dana

7

C (стук) , 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 Насправді розмір коду становить 221 байт (але 212 символів) через символи UNICODE, кодовані в UTF-8. Але tio.run трактує це як 212 байт-код ...

Розмір коду на моєму комп’ютері - 209 символів (218 байт). Але я не зміг замінити \225видимий char у tio.run 😞

Невикористаний код

// 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 190 ° кута «лінії» , щоб відповідати в 2 біта - результат буде тим же самим , і зменшення ширини на 1).

Кодируємо по порядку: ширина (у 2 LSB), TBL , HAT (назад для зворотного циклу). Наприклад , 2 2 1 1 0 для 270 ° кута Т-фігури буде закодований як 1 0 1 2 1(останній 1 є ширина-1 ): 0b0100011001 = 281.

оновлено 12.02:

а) Я перетворив масив у рядок і зберег 18 символів (ви можете побачити попередній код 239 байтів ) :))

б) Більше оптимізація, код зменшується на 9 знаків.
Це моя остання спроба (я так думаю, хаха !) 😀


1
Можна вдарити за допомогою <s> ... </s>.
Джонатан Фрех


О, круто. Спасибі. Лол :))
Джин X

Оце Так! Тетріс низького рівня
Рустем Б.

TBL - це кількість клітинок фігури під найвищою лінією, яка має лише вільний простір або блоки комірок нижче та над нею (немає вільного простору, а потім комірки). TBL + HAT = висота фігури (на кожному горизонтальному положенні). TBL> 0 і HAT> 0 теж.
Джин X

5

Лист звичайний, 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;}

Спробуйте в Інтернеті!

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

Кожен (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 квадратів зверху або знизу, і ніколи не пропускається більше одного квадрата з обох одночасно. Враховуючи цей набір обмежень, я придумав таке кодування:

// 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

Всі унікальні виходи визначаються та зберігаються в a 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значення автоматично циклічно заповнюються вугіллям. Кожна цифра потім відображає два значення, нахил та загальну висоту, відповідно до наступної таблиці:

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

Перегин і загальна висота відносяться до висот стовпчика таким чином: Враховуючи шматок, який ми хочемо розмістити в заданому положенні e, можливо, розмістити шматок, навіть якщо один із стовпців вище, ніж e. Кількість вільного місця задається навісом. Нова висота стовпчика після розміщення шматка - це просто розміщене положення плюс загальна висота.

Приклад: Припустимо, що ми почнемо з розміщення 5фрагмента у стовпці 1. Оскільки нічого іншого ще немає, тому шматок розміщується у позиції 0, а стовпці 1 і 3 тепер мають висоту 1, а стовпець 2 має висоту 2. Потім ми хочемо розмістити 6фрагмент з 1обертанням у стовпці 0. Тут ми можемо фактично розмістити цю деталь у положенні 0; Хоча стовпчик 1 має висоту 1, шматок має перегин 1, і тому є достатньо місця для його розміщення. Стовпець 0 закінчується висотою 2, а стовпець 1 закінчується висотою 3.

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