Тетрис! Окончательные высоты (день 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вводить в другом порядке?
Нил

@ Нейл, да. Это может быть в любом порядке
Луис Фелипе Де Иисус Муньос

Если мы можем переопределить входные значения, могу ли я взять идентификатор элемента как матрицу, которая представляет форму элементов (без вращения)?
Воплощение Невежества

1
Я думаю, что мы не можем ввести матрицу, представляющую фигуры по двум причинам. Входные данные четко определены: 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]y[0..3]n

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

Только первые записи хранятся в явном виде. Все остальное установлено на .820

Эти записи упакованы как:

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

который расширяется до следующих 82 клевов:

"717433667233ff47173333276611000000000000113213001112221112123333333311133233221211"

Использование шестнадцатеричного в окончательном формате требуется только для двух горизонтальных представлений части , следовательно, в приведенной выше строке.I"ff"

Параметры хэш-функции были перебором таким образом, чтобы оптимизировать начальные и конечные нули. Тот факт, что строку можно сжать еще немного, используя 1e12нули в середине и преобразование из base-16 в base-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

С (лязг) , 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видимый символ в 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 для каждой фигуры и каждого угла поворота:

Ширина TBLs Шляпы
----- ------- -------
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 °

Т-фигура:
  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 °

Cube:
  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>.
Джонатан Фрех


О, круто. Благодарю. Лол :))
Джин Икс

Вот это да! Тетрис низкого уровня Rus
Рустем Б.

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

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 #) , 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 квадратов, пропущенных сверху или снизу, и никогда не бывает более 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значения автоматически заполняются циклически углем. Каждая цифра затем отображается в два значения, выступ и общую высоту, в соответствии со следующей таблицей:

\ 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.