(Ре) Реализация тетриса


43

В духе повторной реализации классических видеоигр , я хотел бы предложить сообществу создать свою лучшую реализацию Tetris .

введите описание изображения здесь
Для справки - скриншот официальной версии NES тетриса.

Обязательные функции

  • Должна существовать разумная система подсчета очков, которая вознаграждает за многострочное выравнивание больше, чем однострочное. Текущий счет должен быть виден постоянно.
  • Следующая часть, которая появится, должна быть указана каким-то образом.
  • Распределение семи тетромино должно быть достаточно равномерным (то есть псевдослучайно выбранным).
  • Пользователь должен иметь возможность вращать текущую фигуру в обоих направлениях, а также ускорять ее спуск.
  • Когда игра закончилась, должно быть четко указано, что игра окончена.
  • Исходный код должен быть структурирован и легко понятен.

Дополнительные возможности

  • Повышение скорости падения после определенного количества прохождений (то есть повышение уровня сложности) и повышение оценки за линию ясно, пропорционально скорости.
  • Сила тяжести. Вы можете выбрать «классическую» гравитацию, при которой блоки могут оставаться плавающими в промежутках, или вы можете выбрать «заполнение потоком» гравитации, в которой блоки, которые были отделены от своего первоначального тетромино через линии очистки, могут выпасть в открытую. пробелы.
  • Высокие оценки с вводом имени.
  • Анимация после очистки линии и / или после получения нового рекорда.

Ограничения

  • Любые используемые библиотеки ( jQuery , PyGame и т. Д.) Должны быть свободно доступны.
  • Размер исходного кода не должен превышать 4096 байт, за исключением пробелов и комментариев. Любые внешние ресурсы (файлы данных, изображения и т. Д.) Будут добавлены к длине кода, исключая любые файлы, которые создаются, например, для высоких результатов.
    Я понимаю, что это довольно произвольное ограничение; моя основная цель - не допускать вставки копий существующих реализаций и поощрять краткость и самодостаточность.

Критерии победы

Этот конкурс будет оценен как конкурс популярности , а это означает, что победителем будет выбран вариант с наибольшим количеством голосов. При голосовании я призываю пользователей подавать любые и все предложения, которые, по их мнению, адекватно отвечают вышеуказанным требованиям.

Победитель будет выбран не ранее, чем через 2 недели после первого действительного решения. Кроме того, я буду предоставлять награду победителю, примерно пропорциональную количеству голосов, полученных за этот вопрос ( 10 * #votesокругляется в большую сторону до ближайших 50). Если после двухнедельного периода будет ничья, период соревнования будет продлен на одну неделю. Если еще будет ничья, я оставляю за собой право провести окончательное голосование.

Пожалуйста, попросите каких-либо разъяснений. Пусть победит лучшая реализация!


@ лось, я имею в виду исходный код. Вопрос был обновлен, чтобы отразить это.
Прим

Привет, я могу вставить и раннюю версию и (может быть) обновить это позже?
Хенрик Мюэ,

@ HenrikMühe Я не возражаю, но он не запустит таймер, если он не соответствует требуемому набору функций.
Примо

Я определенно захочу попробовать это. Вероятно, сделаю это с SDL (хотя я раньше не использовал его, так что это сделает его еще более увлекательным!).
Бен Ричардс,

Если бы я только мог найти свою реализацию тетриса в QBASIC! :)
Бен Ричардс,

Ответы:


19

Попробуйте: http://tetris.muehe.org

Обновление Есть глобальная высокая оценка. Наслаждайтесь избиением или - альтернативно - взломом :-)

Скриншот: тетрис в действии

CoffeeScript и HTML-версия должны соответствовать требованиям, насколько мне известно (и я никогда не играл в тетрис).

$ make stats
4095

Github https://github.com/henrik-muehe/tetris

особенности

  • Экспоненциальная оценка
  • Индикатор следующего куска
  • Честная случайность
  • Вращение, перемещение и выпадение
  • Индикация окончания игры
  • Highscore с сервером бэкэндом , что не должно быть , что легко fakeable.

Большой! Но небольшая ошибка: при заполнении нескольких рядов на дне ямы один из них не исчезает.
manatwork

@manatwork Ты имеешь в виду, когда один из исчезающих рядов находится ближе всего к низу? Я посмотрю на это, скриншот был бы потрясающим :)
Хенрик Мюэ

Это очень хорошая реализация. Один запрос: не могли бы вы перечислить, какие функции реализованы? Чем больше будет представлено решений, тем проще будет сравнение.
Прим

1
@ user2023370, это байты, хотя ...
Хенрик Мюэ

1
Ссылка не работает.
Эрик Outgolfer

18

паскаль

Разработан в FreePascal 2.6.2, должен компилироваться и с Turbo Pascal 6.0. Используется только блок Crt , никаких внешних ресурсов.

program tetris;

  uses
    Crt;

  const
    width = 10;                                  { playing field width }
    height = 20;                                 { playing field height }

  const
    piece: array [1..7, 1..3, 0..1] of ShortInt = ( { piece shapes : piece, element, coordinate }
      (( 1, 0), ( 1, 1), ( 0, 1)),               { O }
      ((-1, 0), ( 1, 0), ( 2, 0)),               { I }
      (( 0, 1), ( 1, 0), ( 2, 0)),               { L }
      (( 0, 1), (-1, 0), (-2, 0)),               { J }
      ((-1, 0), ( 1, 0), ( 0, 1)),               { T }
      (( 1, 0), ( 0, 1), (-1, 1)),               { S }
      ((-1, 0), ( 0, 1), ( 1, 1))                { Z }
    );
    color: array [1..7, 0..1] of Byte = (        { piece colors : foreground, background }
      (Yellow,       Brown),                     { O }
      (LightCyan,    Cyan),                      { I }
      (LightBlue,    Blue),                      { L }
      (White,        LightGray),                 { J }
      (LightMagenta, Magenta),                   { T }
      (LightGreen,   Green),                     { S }
      (LightRed,     Red)                        { Z }
    );

  var
    area: array [1..width + 2, 1..height + 1] of Byte; { playing field }
    coord: array [0..3, 0..1] of Byte;           { precalculated element coordinates }
    played,                                      { played pieces count }
    removed,                                     { completed lines count }
    level,                                       { current level }
    score: LongInt;                              { accumulated score }
    time,                                        { current level delay }
    wait,                                        { current piece delay }
    made,                                        { completed lines in current level }
    multi,                                       { multiline count }
    screen,                                      { original screen size }
    i, j, j2: Word;                              { counters }
    current,                                     { current piece }
    next,                                        { next piece }
    x,                                           { horizontal coordinate }
    y,                                           { vertical coordinate }
    position,                                    { rotation position }
    k: Byte;                                     { pressed key }
    ok: Boolean;                                 { aggregated condition }

{ precalculates the give piece's elements coordinates }
  procedure coordinate(current, x, y, position: Byte);
  begin
    coord[0, 0] := x;
    coord[0, 1] := y;
    for i := 1 to 3 do begin
      coord[i, 0] := x + piece[current, i, position mod 2] * (Ord(position in [0, 1]) * 2 - 1);
      coord[i, 1] := y + piece[current, i, 1 - position mod 2] * (Ord(position in [0, 3]) * 2 - 1);
    end;
  end;

{ draws a piece }
  procedure draw(current, x, y, position: Byte; visible: Boolean);
  begin
    coordinate(current, x, y, position);

    for i := 0 to 3 do begin
      GotoXY(coord[i, 0] * 2 + 1, coord[i, 1]);
      if visible then begin
        TextColor(color[current, 0]);
        TextBackground(color[current, 1]);
        Write('[]');
      end else begin
        TextBackground(Black);
        Write('  ');
      end;
    end;
  end;

{ check whether a piece can be placed in given position }
  function check(x2, y2, position2: Byte): Boolean;
  begin
    coordinate(current, x2, y2, position2);

    ok := True;
    for i := 0 to 3 do if area[coord[i, 0], coord[i, 1]] <> 0 then ok := False;

    if ok then begin
      x := x2;
      y := y2;
      position := position2;
    end;

    check := ok;
  end;

begin

  Randomize;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;
  screen := WindMax;

  for i := 0 to width + 1 do for j := 1 to height + 1 do area[i, j] := 9 * Ord(not ((i in [1..width]) and (j in [1..height])));

  Window(1, 1, width * 2 + 4, height + 2);
  for i := 1 to (width + 2) * (height + 1) do Write('##');
  Window(3, 1, width * 2 + 2, height);
  ClrScr;
  Window(1, 1, Lo(screen), Hi(screen));

  Window(width * 2 + 7, 1, width * 2 + 25, 10);
  Writeln('Next');
  Writeln;
  Writeln;
  Writeln('Piece');
  Writeln('Line');
  Writeln('Level');
  Writeln('Score');
  Window(1, 1, Lo(screen), Hi(screen));

  played := 0;
  removed := 0;
  level := 1;
  score := 0;

  current := 9;
  next := 9;
  made := 0;

  repeat

    if current = 9 then begin
      if next = 9 then next := Random(7) + 1 else draw(next, width + 9, 1, 0, False);

      current := next;
      next := Random(7) + 1;
      x := width div 2;
      y := 1;
      position := 0;
      time := 1100 - level * 100;
      wait := time;

      Inc(played);
      Inc(score);
      if made = 25 then begin
        Inc(level);
        made := 0;
      end;

      draw(next, width + 9, 1, 0, True);

      Window(width * 2 + 15, 4, width * 2 + 25, 10);
      TextColor(LightGray);
      TextBackground(Black);
      Writeln(played:5);
      Writeln(removed:5);
      Writeln(level:5);
      Writeln(score:5);
      Window(1, 1, Lo(screen), Hi(screen));

      if not check(x, y, position) then begin
        GotoXY(width * 2 + 7, 10);
        Write('Game Over');
        Break;
      end;
    end;

    draw(current, x, y, position, True);
    GotoXY(1, 1);
    repeat Delay(1);
      Dec(wait);
    until KeyPressed or (wait = 0);
    draw(current, x, y, position, False);

    if KeyPressed then begin
      k := Ord(ReadKey);
      case k of
        75, 77: check(x + Ord(k = 77) * 2 - 1, y, position);
        72, 80: check(x, y, (position + Ord(k = 80) * 2 + 1) mod 4);
        32: begin
          time := 1;
          Inc(score);
        end;
      end;
    end;

    if wait = 0 then begin
      if not check(x, y + 1, position) then begin
        draw(current, x, y, position, True);
        for i := 0 to 3 do area[coord[i, 0], coord[i, 1]] := current;

        multi := 0;
        for j := 1 to height do begin
          ok := True;
          for i := 1 to width do if area[i, j] = 0 then ok := False;

          if ok then begin
            for j2 := j downto 2 do for i := 1 to width do area[i, j2] := area[i, j2 - 1];
            for i := 1 to width do area[i, 1] := 0;

            Inc(score, 10 + multi * 2);
            Inc(removed);
            Inc(multi);

            Window(3, 1, width * 2 + 2, height);
            TextBackground(Black);
            GotoXY(1, j);
            DelLine;
            GotoXY(1, 1);
            InsLine;
            Window(1, 1, Lo(screen), Hi(screen));
          end;

        end;
        if multi <> 0 then Inc(made);

        current := 9;
      end;
      wait := time;
    end;

  until k = 27;

  ReadKey;
  TextColor(LightGray);
  TextBackground(Black);
  ClrScr;

end.

Скриншот

Тетрис в Паскале

(В Linux в окне XTerm.)

контроль

  • Left - движение влево
  • Right - переместить вправо
  • Up - вращать против часовой стрелки
  • Down - вращаться по часовой стрелке
  • Space - падать
  • Esc - выход

счет

  • сыгранная пьеса - 1 балл
  • выпавший кусок - 1 балл
  • завершенная линия - 10 баллов
  • несколько строк - множитель * 2 балла

Уровень начинается с 1 и увеличивается после завершения каждых 25 строк. (Несколько завершенных строк считаются как 1.)

измерение

bash-4.2$ sed '
s/{[^{}]*}//g                  # remove comments
s/^ *\| *$//g                  # trim leading and trailing spaces
/^$/d                          # remove empty lines
s/ *\([:=<>,;+*-]\+\) */\1/g   # no space around operators
' tetris.pas | wc -c
3697

17

Ява (Свинг)

Это реализация первого исторического издания Game Boy от Nintendo (c) 1989 года.

Game Boy Tetris Nintendo

Как играть:

Z= повернуть влево
X= повернуть вправо
Left= двигаться влево
Right= двигаться вправо
Down= двигаться вниз (медленно)
Up= повернуть влево (просто для удобства использования)
R= сброс игры

Я избегал использовать более одного класса (потому что мне приходит в голову аспект гольфа). Но теперь он больше не пригоден для игры в гольф в любом случае ... Тем не менее, я сжал и закодировал Base64 один Font-File и один Image-File, так что я могу использовать его в одном-единственном файле класса.

Чтобы запустить его, скопируйте код Java в вашу среду IDE и запустите. Вам не нужны никакие дополнительные библиотеки или ресурсы.

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.zip.ZipInputStream;

import javax.imageio.ImageIO;
import javax.swing.JOptionPane;
import javax.xml.bind.DatatypeConverter;

public class JTetris extends javax.swing.JFrame {
    static Image IMAGE;
    static Font FONT;

    static {
        ZipInputStream zipImage = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIABmiWkRZ0oOouwAAAJYKAAAFAAAAYy5ibXBz8p3GxQAGZUCsAcQ3gFgCiBkZWMDiCkB5GBCA0iJyLAwl+3oYYpJsGK58u8NAKjBGAgyUgVG7QHYxKUGBAjDiBJUUQUhQAMQGA3xsFL3MMBMNRrpdEABhQxQLCiDYmHZBxCF2QdhAu8CyEHLk2cWkBJSH" +
            "xxcoCgyg5hsbAgWhbBgJUg9Wg24XWDGQHJF2QeOCUruQwEi0S0kRe5qHKCY2zQsgkv0ItAsIKC97EQnecGTYRQUwKNsbNAYAUEsBAj8AFAAAAAgAGaJaRFnSg6i7AAAAlgoAAAUAJAAAAAAAAAAgAAAAAAAAAGMuYm1wCgAgAAAAAAABABgAkJCiSyczzwGQkKJLJzPP" +
            "AZCQoksnM88BUEsFBgAAAAABAAEAVwAAAN4AAAAAAA=="
        )));

        ZipInputStream zipFont = new ZipInputStream(new ByteArrayInputStream(DatatypeConverter.parseBase64Binary(
            "UEsDBBQAAAAIAEq5UEN0MGyWoggAAHgrAAAFAAAAYS50dGbklsvvC1EUx78zU+/3OxHiEuLd1jMlxKNR70cQCxumY9oOnU5Np6osdIGEDYmwZGElkYh/gI2FBUIsvFdILNgh8Rxft4d6RyxEYvq7PZ97es73nHNv8mthAOiJFiyotRtS0xMnum4Gej0HsNXx7erbI6+7" +
            "ks8CxqNiuVk48qTbVKB3BIy8XXLtbVv6x0cBkwuzS3SYrffvuL8MYGzJj3av3BfP4P4p8yeUA8cGxqWBSau4n+Lbu6tYET8AEoMBqIrtuzuPz33D/Uyg28VqUItgxtTrvRGAAoW4LK7BsHTnb5HAE9qB6E+PidGYhCTmYjP24AxuxTGYR98UpLEIdtsXP4zvxXfjO/HN" +
            "+Gp8Kg6p9O8//8ucRns93ZK6vqXfvBccXLuv7b/QH2LjVtyyYLUAdIfJaMmz6Gc83w3yd9OuSLS0WmcxRLj1xYJY/Gz9Wcyfr79XJ6aO3MEKTEAPWsDE988Y4yQM4RGAsEEeIWyiGzLCFsZjgXACY1EQ7oJeOCzclZnHhbthHs4Ld8cQPBPugT6GKdwT841+wr0wzFgl" +
            "3BtpwxHugxHGMeG+mGycEx6EicbdNhusYFqwYCQ4LTJmL2GDnBU20dfcK2xhjXlIOIFl5i3hLhhqjRLuiow1S7gbdlu+cHdMsh4L98DwRD/hntiTmCjcC8nEaeHe2Jq4IdwHmS7ThPtiUxdPeBDWd7nYZgPo33VANqg2Q69YitQ6Owo9Z4daZdd3uGp6etqMDfWqG6rV" +
            "dugF9Fa2qQlL+T9XLQ6aE9V6t1gv22EuqEQborDuROoX0b/87JPUJjeseUFFTUumdfhUHT71Y/hUHT71Y/jULyp6NWWrKLS3ub4d7lBBQeU2LFc6oBRU1fJK5IYVO6KoXVZL/fyyUhRV56ZSBUbUtETSCfyv5h5+6fWveh1+6ZVq2DWVr3vlSDW8qKQ6/fQU+UajkdQl" +
            "2MTHAj+s+sWW/dVSDG6kMpnMjGlzsqHLpne5Khv4Pj9TiyJ2mK9/DBQtR0KcdkQyCIupsue4lZpbS+WbqRnJdCr3USLvFdXOuu3s8CpFtcetlpphTW0P2Lzf5CS7Vd7dli0uWbStXIqQRYAqmgjhoYgSIiisg41Iexzs4H4V93WSS56ONKZhBjbQU6UnpG81bB0dSGwF" +
            "20gTsJTs66zFCFhjImk990XmlnVODgGjI6qxHr0Orfoz7T/P+66rTVqjprMrUJw3ifQX6lM76mRRJ31WJ4s66cczetRXjFbaazPfha9VeeLMKPA9x5zltB2FEqlKz3K9Zy+0NomdaCpDsQcfeSzTdxkxei5SfBVEo9bpglM59Po/v281HJfw+k/PVWe/oqcBW0+bp47H" +
            "HiPt82hLpB+dT89vum/oV7IzhZyETPD7s/7wUzk/elKi3CBl9GsGb38Osox05aR36SmzWs2XPIVFiOQMOSVE8Zu+nG9UnK80krQhiowr65twtdfVXeXR5PsMxqRpc5+7yJOKtDtZ09a356GiPXvgsm6JeaHubzsCOXkfTbmT3VrB5Q1mmbMEi0hl3TNM86VxFF2QMOXL" +
            "XVsuNnEF8mtAHquDQG7DxvVYSOFbbbfVQovG0NwOVV/kFKj3g4eVJZ5/3dALAzGUIl8/MMTDAvEBAC9oe9Hu/WJ/kD9s78f3gfdvpKrPs6H/A+1VkJtADAMnWXFAqEIVqjhw6KEfzQvan/Ud/cQmJdnRjoyzBLUCFAxeZ8aOHSfgk3aU+Kp6oFqX7/mn2s5L6KF+JPoC" +
            "8QOVOqdFAqiOpICIqxbYo82LiTEcccIZF7zjgyiD14JJ9MrUfmsUWAlrx3nybJXJ/b6v1/yIene+vjHV+OQrxNUsQ5XCpSYvtZBCXZeWD85FbA8CGE3TXXlkQwTIJkI1cvQrGmlXFAOiWYslTyXZJ1XX5JWTeT8IXbEKNaBKky2OmVbG17et7AMet8D6Vlh1kZq8sgdj" +
            "RckB7b9Tnx3ELVCmouKUtBHriQZczDu8dlk1i5w+SwGrFYp5mvtc03aE9NdGqP2rSNu3slZt6Ea4zTmuQc/Zcu1q0XEqj52KR/J9YIIwfN4gZOREc/Xdi+O4W6/anb3a9dGGxBoe1NRf8szqSdveBHqRE8SK4PtAbHr2cXFqh9rORFwMcgVACL6qgdi+WY9mzbjJ1agC" +
            "A2Uh8jgnE1wOHu0lvY4L+RGIZCzMHsnS+bwfbljlmyIz/X2Cyaf8t0jUNzue7CR+ZC32wD0kgyL537VkJY/XUtXl4/f3DWJDnotxhmJ67IyMMD2if9qO9ovOCYvrzqWCIf5Z+I4H4iCiODiKdFob37e6M40fO8Cyqtp4cxjXh5iNz2KXn84f5pI65vQ5nKrUWbPJ+fy7" +
            "nMF/ARx6tzdmc0q43dv7n3IcnZlxcHefezcFAJt3de7FX/bLHQWAEAaiuPe/srhiMcEncWDZMkUKwe/oJM+R+Ip1IqfPB1WCakTEbi7q6Jyb9sZ5oAgoYThNGjp24i9uhItx5j6DbyffdZC4XuWaY4gwHjnb1m/m0cxzUENOUK/VVs+f/S1qh79xC6dP/B+YfgEZgCsT" +
            "XRT0T4OPuu6oXXK5r1OxdrBO2xkcikrZz/l3jjozreH9LYpVi1WLVYtVi1VfjbZVGUfbqqNt1dG26mhbFaBB21blBJIgO+HzavBZNXWgKUQCsK8w58oQKQJ19BhR5mHMyUHYuOfWEHOEmL7+B49bDP3Dc/4Q0DW9aSsURmEYfqGx97+Po31eu4MFYdglBEHw47qIMAiD" +
            "YEEQBAuCYF1AGIZBGIRBGARB0MvBA3N8MMccc3wAkN1Zu2umvfeihkba+ueezEstLSxnogE8JMpk9thQDqGsVIWO8JSq0Bmeo/bwEjTUCV57Mn/ragPvUZkO1iWV+8u5rvDRl/NnRyv4CoqawndQ1Bx+0FhL+G0q1xX+oi6QDOAftTWBStAQqh0VUKtBvaTdDVBLAQI/" +
            "ABQAAAAIAEq5UEN0MGyWoggAAHgrAAAFACQAAAAAAAAAIAAAAAAAAABhLnR0ZgoAIAAAAAAAAQAYAABukYC8ys4BJXTvDjUzzwEldO8ONTPPAVBLBQYAAAAAAQABAFcAAADFCAAAAAA=")
        ));

        try {
            zipImage.getNextEntry();
            zipFont.getNextEntry();
            IMAGE = ImageIO.read(zipImage);
            FONT = Font.createFont(0, zipFont);
            FONT = FONT.deriveFont(20.0f);
        } catch (Exception e) {
            System.exit(1);
        }
    }

    int[] speeds = {887,820,753,686,619,552,468,368,284,184,167,150,133,117,100,100,83,83,66,66,50};
    int[] points = {40,100,300,1200};
    int[][][] stones = {
        {{0,1},{1,1},{2,1},{0,2}},      // L 0
        {{0,1},{1,1},{2,1},{3,1}},      // I 1
        {{0,1},{1,1},{2,1},{2,2}},      // J 2
        {{0,1},{1,1},{2,1},{1,2}},      // T 3
        {{0,1},{1,1},{1,2},{2,2}},      // Z 4
        {{1,1},{2,1},{0,2},{1,2}},      // S 5
        {{1,1},{2,1},{1,2},{2,2}}       // O 6
    };
    Color[] colors = {
        new Color(220, 246, 212), // light
        new Color(140, 190, 116), // green
        new Color(60, 98, 92),    // blue
        new Color(4, 30, 20)      // dark
    };
    int FREE = 0;
    int BORDER = 8;
    int DELTA = 20;
    int WIDTH = 10;
    int HEIGHT = 20;
    int SIZE = 24;

    int[][] field = new int[WIDTH][HEIGHT];
    int[][] stone;
    int deltaX;
    int deltaY;
    int curStone;
    int nextStone = new Random().nextInt(7);
    int score;
    int level;
    int lines;
    int steps;
    int speed = speeds[0];

    Timer tickTimer = new Timer();
    Image dbImage;
    Image staticImage;
    Graphics dbg;

    int anim = -1;
    boolean blink = false;
    ArrayList<Integer> removeLinesRows = new ArrayList<Integer>();
    boolean keyDown = true;
    boolean freezeKeys = false;

    private JTetris() {
        setDefaultCloseOperation(2);
        setResizable(false);
        setSize(475, 480);

        addPropertyChangeListener("isDirty", new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent e) {
                repaint();
            }
        });

        reset();

        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                super.windowClosed(e);
                tickTimer.cancel();
            }
        });

        addKeyListener(new KeyAdapter() {
            @Override
            public void keyReleased(KeyEvent e) {
                keyDown = true;
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (freezeKeys) {
                    keyDown = false;
                    return;
                }

                switch (e.getKeyCode()) {
                    case 82: reset(); break;
                    case 37: updateField(-1, 0, false); break;
                    case 39: updateField(1, 0, false); break;
                    case 40: if (keyDown) updateField(0, 1, true); break;
                    case 38:
                    case 90: rotate(false); break;
                    case 88: rotate(true);
                }
            }
        });

        setVisible(true);
    }

    private void startTickTimer() {
        if (tickTimer != null) {
            tickTimer.cancel();
        }

        freezeKeys = false;

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                updateField(0, 1, false);
            }
        }, 0, speed);
    }

    private void startRemoveLineTimer() {
        if (!removeLinesRows.isEmpty()) {
            anim = 7;
        }

        if (tickTimer != null) {
            tickTimer.cancel();
        }

        tickTimer = new Timer();
        tickTimer.schedule(new TimerTask() {
            public void run() {
                if (anim > 0) {
                    blink = !blink;
                    firePropertyChange("isDirty", false, true);
                }

                if (anim-- < 0) {
                    blink = false;
                    tickTimer.cancel();
                    if (removeLinesRows.size() > 0) {
                        int delta = 0;
                        for (int row : removeLinesRows) {
                            for (int r = row + delta++; r > 2; r--) {
                                for (int c = 0; c < WIDTH; c++) {
                                    field[c][r] = field[c][r - 1];
                                }
                            }
                        }

                        score += points[removeLinesRows.size() - 1] * (level + 1);
                        lines += removeLinesRows.size();
                        level = lines / 10;
                        speed = speeds[level > 20 ? 20 : level];
                        removeLinesRows.clear();
                    }

                    createNextStone();

                    startTickTimer();
                }
            }
        }, 50, 160);
    }

    public static void main(String[] a) {
        new JTetris();
    }

    @Override
    public void paint(Graphics g) {
        if (dbImage == null) {
            dbImage = createImage(getWidth(), getHeight());
            dbg = dbImage.getGraphics();
            dbg.setFont(FONT);

            staticImage = createImage(getWidth(), getHeight());
            Graphics sg = staticImage.getGraphics();
            sg.setFont(FONT);

            sg.setColor(colors[3]);
            sg.fillRect(0, 0, getWidth(), getHeight());

            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 70, 170, 58);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 73, 170, 52);
            sg.setColor(colors[0]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 95, 170, 27);
            sg.setColor(colors[2]);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 98, 170, 3);

            sg.setColor(colors[1]);
            sg.fillRoundRect(40 + 13 * SIZE - 15, 40 + 13 * SIZE - 15, SIZE * 4 + 30, SIZE * 4 + 30, 20, 20);
            sg.setColor(colors[0]);
            sg.fillRoundRect(40 + 13 * SIZE - 12, 40 + 13 * SIZE - 12, SIZE * 4 + 24, SIZE * 4 + 24, 15, 15);

            sg.fillRoundRect(330 - 6, 60 - 6, 120 + 12, 25 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 155 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.fillRoundRect(330 - 6, 235 - 6, 120 + 12, 60 + 12, 15, 15);
            sg.setColor(colors[2]);
            sg.fillRoundRect(40 + 13 * SIZE - 9, 40 + 13 * SIZE - 9, SIZE * 4 + 18, SIZE * 4 + 18, 15, 15);

            sg.fillRoundRect(330 - 3, 60 - 3, 120 + 6, 25 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 155 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.fillRoundRect(330 - 3, 235 - 3, 120 + 6, 60 + 6, 15, 15);
            sg.setColor(colors[3]);
            sg.fillRoundRect(40 + 13 * SIZE - 6, 40 + 13 * SIZE - 6, SIZE * 4 + 12, SIZE * 4 + 12, 15, 15);

            sg.setColor(colors[0]);
            sg.fillRoundRect(330, 60, 120, 25, 5, 5);
            sg.fillRect(40 + WIDTH * SIZE + SIZE, 40, 3, (HEIGHT - 2) * SIZE);
            sg.fillRoundRect(40 + 13 * SIZE - 3, 40 + 13 * SIZE - 3, SIZE * 4 + 6, SIZE * 4 + 6, 5, 5);
            sg.fillRoundRect(330, 155, 120, 60, 15, 15);
            sg.fillRoundRect(330, 235, 120, 60, 15, 15);

            sg.setColor(colors[3]);
            sg.drawString("SCORE", 340, 80);
            sg.drawString("LEVEL", 340, 180);
            sg.drawString("LINES", 340, 260);

            for (int r = 0; r < (HEIGHT - 2) * 24 / 18; r++) {
                sg.drawImage(IMAGE, 16, 40 + r * 18, 16 + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
                sg.drawImage(IMAGE, 40 + WIDTH * SIZE, 40 + r * 18, 40 + WIDTH * SIZE + SIZE, 58 + r * 18, 8 * SIZE, 0, 9 * SIZE, 18, this);
            }
        }

        dbg.drawImage(staticImage, 0, 0, this);

        dbg.setColor(colors[3]);
        dbg.drawString(String.format("%6s", score > 999999 ? 999999 : score), 320, 120);
        dbg.drawString(String.format("%4s", level > 20 ? 20 : level), 340, 205);
        dbg.drawString(String.format("%4s", lines > 9999 ? 9999 : lines), 340, 285);

        for (int x = 0; x < 4; x++) {
            drawField(dbg, stones[nextStone][x][0] + 13, stones[nextStone][x][1] + 13, nextStone + 1);
        }

        for (int r = 2; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                drawField(dbg, c, r - 2, field[c][r]);
            }
        }

        if (blink) {
            if (anim == 0)  {
                dbg.setColor(colors[0]);
            } else {
                dbg.setColor(colors[1]);
            }

            for (int row : removeLinesRows) {
                dbg.fillRect(40, 40 + (row - 2) * SIZE, SIZE * WIDTH, SIZE);
            }
        }

        g.drawImage(dbImage, 0, 0, this);
    }

    private void drawField(Graphics g, int x, int y, int idx) {
        if (idx > DELTA) {
            idx -= DELTA;
        }

        g.drawImage(IMAGE, 40 + x * SIZE, 40 + y * SIZE, SIZE + 40 + x * SIZE, SIZE + 40 + y * SIZE, idx * SIZE, 0, (idx + 1) * SIZE, SIZE, this);
    }

    private void createNextStone() {
        curStone = nextStone;
        stone = stones[curStone];
        deltaX = (WIDTH + 1) / 2 - 2;
        deltaY = 1;

        if (!isMoveable(0, 0)) {
            tickTimer.cancel();
            JOptionPane.showMessageDialog(this, "Game over! Press OK for a new game.");
            reset();
            return;
        }

        updateField(0, 0, false);
        nextStone = new Random().nextInt(7);
    }

    private void reset() {
        score = 0;
        steps = 0;
        lines = 0;
        level = 0;
        speed = speeds[0];

        for (int r = 0; r < HEIGHT; r++) {
            for (int c = 0; c < WIDTH; c++) {
                field[c][r] = FREE;
            }
        }

        createNextStone();
        startTickTimer();
    }

    private void updateField(int hor, int ver, boolean player) {
        if (isMoveable(hor, ver)) {
            if (player) {
                steps++;
            }

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
            }

            deltaX += hor;
            deltaY += ver;

            for (int x = 0; x < 4; x++) {
                field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
            }

            firePropertyChange("isDirty", false, true);
        } else if (ver == 1) {
            freezeKeys = true;
            fixStone();
            removeLines();
        }
    }

    private void removeLines() {
        outer:
        for (int r = HEIGHT - 1; r >= 0; r--) {
            for (int c = 0; c < WIDTH; c++) {
                if (field[c][r] == FREE) {
                    continue outer;
                }
            }

            removeLinesRows.add(r);
        }

        startRemoveLineTimer();
    }

    private void fixStone() {
        score += steps;
        steps = 0;

        for (int x = 0; x < 4; x++) {
            field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1 + DELTA;
        }
    }

    private boolean isMoveable(int hor, int ver) {
        for (int x = 0; x < 4; x++) {
            int c = stone[x][0] + deltaX + hor;
            int r = stone[x][1] + deltaY + ver;

            if (c < 0 || c >= WIDTH || r < 0 || r >= HEIGHT) {
                return false;
            }

            if (field[c][r] > DELTA) {
                return false;
            }
        }

        return true;
    }

    private void rotate(boolean r) {
        if (curStone == 6) { // O
            return;
        }

        synchronized (field) {
            int[][] o = new int[4][2];

            for (int x = 0; x < 4; x++) {
                o[x] = new int[] { r ? 3 - stone[x][1] : stone[x][1], r ? stone[x][0] : 3 - stone[x][0] };
            }

            if (canRotate(o)) {
                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = FREE;
                }

                stone = o;

                for (int x = 0; x < 4; x++) {
                    field[stone[x][0] + deltaX][stone[x][1] + deltaY] = curStone + 1;
                }

                firePropertyChange("isDirty", false, true);
            }
        }
    }

    private boolean canRotate(int[][] o) {
        for (int x = 0; x < 4; x++) {
            if (o[x][0] + deltaX < 0 ||
                o[x][0] + deltaX >= WIDTH ||
                o[x][1] + deltaY >= HEIGHT ||
                field[o[x][0] + deltaX][o[x][1] + deltaY] > DELTA) {
                return false;
            }
        }

        return true;
    }
}

Todos:

  • быстрые камни на более высоком уровне (сделано!)
  • оригинальная анимация удаления линии (готово!)
  • правильные точки для линий (готово!)
  • прервать движение вниз по новому камню (готово!)
  • экран рекордов
  • оригинальное поведение при вращении
  • меню для выбора уровня
  • экран игры
  • аудио

Комментарии приветствуются :)


2
Мне действительно очень нравится этот. Одно из предложений для удобства использования: когда пользователь удерживает клавишу ↓, чтобы отбросить блок, программно «отпустите» эту клавишу после приземления блока (то есть пользователю потребуется отпустить клавишу, а затем снова нажать ее, чтобы ускорить следующий блок). ,
Примо

Большой! Я уже думал о том, как справиться с этим. Это будет хороший метод! Благодарность!
bobbel

1
+1 За то, что заставил меня визжать, как восьмилетний, с его первым Nintendo GameBoy!
WallyWest

Не уверен, что ужасный код Java ужасен или ретро. +1 в любом случае. ;)
Два

Для любого класса, который используется один раз и только один раз, вы гарантированно сократите свой код, не импортируя его, а вместо этого используя полное имя в коде. Например, удалите import java.awt.event.KeyAdapter;и измените new KeyAdapterна new java.awt.event.KeyAdapter.
Бен Голдберг

8

Луа - 2876

Тетрис в терминале, работает на большинстве систем Unix, чистый lua, никаких дополнительных библиотек не требуется.

Управление: Wasd или HJKL, W / K, чтобы бросить, S / J, чтобы вращаться, AD / HL, чтобы двигаться

Скорость увеличивается со счетом, при удалении нескольких строк вы получаете квадрат количества уничтоженных линий.

Это не самое удачное решение для игры в гольф, но я все равно решил немного поиграть в него. Символы новой строки просто соответствуют тексту в 80 столбцов, я не включил их в число символов.

math.randomseed(os.time())if arg[1]then local function s(e)local e=io.popen(
"sleep "..e)e:read"*a"return e:close()end local l={{0,1,1,0,1,1},{-1,1,-1,0,1,0}
,{-1,0,1,0,1,1},{-1,0,0,1,1,0},{-1,1,0,1,1,0},{-1,0,0,1,1,1},{-1,0,1,0,2,0}}
function T(t,e,o)if o>1 then return T(-e,t,o-1)end return t,e end local t 
function fit(i,n,r,o)if n<1 or n>10 or r<1 or t[r][n]~=0 then return end for e=1
,6,2 do local e,o=T(l[i][e],l[i][e+1],o)e,o=e+n,o+r if e<1 or e>10 or o<1 or((t[
o]or{})[e]or 0)~=0 then return end end return i end function put(e,r,i,n)t[i][r]
=e for o=1,6,2 do local n,o=T(l[e][o],l[e][o+1],n)n,o=n+r,o+i;(t[o]or{})[n]=e 
end end while 1 do os.execute"clear"io.write("\27[0;37m+----------++----+\r\n"..
("|          ||    |\r\n"):rep(2)..
"|          |+----+\r\n|          |Score:\r\n"..("|          |\r\n"):rep(16)..
"+----------+\27[20F\27[C")t={}for e=1,20 do t[e]={}for o=1,10 do t[e][o]=0 end 
end local e,o,f,r,a,c,n=20,5,0,math.random(7),math.random(7),0,1 while 1 do f=f+
1 s(.1)local d=io.open(arg[1],"rb")or os.exit(0,io.write"\27[49;39m",io.stdout:
flush(),os.execute"clear")local i=d:read(1)while i do if i=="a"or i=="h"then if 
fit(r,o-1,e,n)then o=o-1 end elseif i=="d"or i=="l"then if fit(r,o+1,e,n)then o=
o+1 end elseif i=="w"or i=="k"then while fit(r,o,e-1,n)do e=e-1 end elseif i==
"s"or i=="j"then if fit(r,o,e,n+1)then n=n%4+1 end end i=d:read(1)end d:close()d
=io.open(arg[1],"w")d:write()d:close()if f%(math.max(1,10-math.floor(c/20)))==0 
then if fit(r,o,e-1,n)then e=e-1 else put(r,o,e,n)r,e,o,a,n=a,20,5,math.random(7
),1 if not fit(r,o,e,n)then break end local e=1 local o=0 while e<21 do local n=
1 for o=1,10 do n=n*t[e][o]end if n~=0 then table.remove(t,e)local e={}for o=1,
10 do e[o]=0 end table.insert(t,e)o=o+1 else e=e+1 end end c=c+o*o end end put(r
,o,e,n)for e=20,1,-1 do for o=1,10 do io.write("\27[3"..t[e][o].."m"..(t[e][o]
==0 and" "or"#"))end io.write"\27[E\27[C"end io.write("\27[20F\27[13C\27[3",a,
"m")for e=1,0,-1 do for n=-1,2 do local o for t=1,6,2 do if l[a][t]==n and l[a][
t+1]==e then io.write"#"o=e end end if e==0 and n==0 then io.write"#"o=e end if 
not o then io.write" "end end io.write"\27[4D\27[B"end io.write(
"\27[2B\27[D\27[49;37m",c,"\27[4F\27[C")t[e][o]=0 for i=1,6,2 do local r,n=T(l[r
][i],l[r][i+1],n)r,n=r+o,n+e;(t[n]or{})[r]=0 end end io.write"\27[49;39m"io.
stdout:flush()os.execute"clear"io.write
"\aGame over. Will automatically start new game in 5 seconds. Use ^C to exit"s(5
)io.stdout:flush()end else local e=os.tmpname()local o=io.open(e,"w")local n=
newproxy(true)getmetatable(n).__gc=function()os.execute"stty -raw echo"end os.
execute"stty raw -echo"local t=""i=0 while arg[i]do t=arg[i].." "..t i=i-1 end 
os.execute(t..e.." &")while 1 do o:close()local t=io.read(1)o=io.open(e,"a")o:
write(t)if t=="\3"then o:close()os.remove(e)os.execute"stty -raw echo"
getmetatable(n).__gc=nil os.execute"sleep 0.1"os.exit()end end end

тетрис в терминале


3
У вас случайно не есть версия без гольфа?
nyuszika7h

7
@ nyuszika7h Что ты имеешь в виду? Он написал это так, используя cat.
Камило Мартин

1
@CamiloMartin что cat, я использую
Butterfiles

3
@mniip Ах да! Добрый день C-x M-c M-butterfly...
Камило Мартин

6

Mathematica

Этот код был написан в Mathematica Xiangdong Wen и на самом деле может быть воспроизведен в веб-браузере здесь: Shape Descender (нажмите на графику, чтобы начать стрелки). Ниже приведен снимок экрана и полный код, что довольно мало для полного веб-приложения этой игры.

введите описание изображения здесь

Код

allBlocks = {{{{1, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 1, 1, 0}, {0, 1, 0, 0}, {0, 1, 0, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {1, 1, 1, 0}, {0, 0, 1, 0}, {0, 0, 0, 
      0}}, {{0, 1, 0, 0}, {0, 1, 0, 0}, {1, 1, 0, 0}, {0, 0, 0, 
      0}}}, {{{0, 2, 0, 0}, {2, 2, 2, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 2, 0, 0}, {0, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {2, 2, 2, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}, {{0, 2, 0, 0}, {2, 2, 0, 0}, {0, 2, 0, 0}, {0, 0, 0, 
      0}}}, {{{0, 0, 3, 0}, {3, 3, 3, 0}, {0, 0, 0, 0}, {0, 0, 0, 
      0}}, {{0, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 3, 0}, {0, 0, 0, 
      0}}, {{0, 0, 0, 0}, {3, 3, 3, 0}, {3, 0, 0, 0}, {0, 0, 0, 
      0}}, {{3, 3, 0, 0}, {0, 3, 0, 0}, {0, 3, 0, 0}, {0, 0, 0, 0}}},
   {{{0, 0, 0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 
      0, 0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}, {{0, 0, 0, 
      0}, {0, 4, 4, 0}, {0, 4, 4, 0}, {0, 0, 0, 0}}}, {{{0, 0, 0, 
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5, 
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}, {{0, 0, 0, 
      0}, {5, 5, 5, 5}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 5, 
      0}, {0, 0, 5, 0}, {0, 0, 5, 0}, {0, 0, 5, 0}}}, {{{6, 6, 0, 
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6, 
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}, {{6, 6, 0, 
      0}, {0, 6, 6, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 0, 6, 
      0}, {0, 6, 6, 0}, {0, 6, 0, 0}, {0, 0, 0, 0}}}, {{{0, 7, 7, 
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0, 
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}, {{0, 7, 7, 
      0}, {7, 7, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 0}}, {{0, 7, 0, 
      0}, {0, 7, 7, 0}, {0, 0, 7, 0}, {0, 0, 0, 0}}}};
smallBoard = 
  Table[If[i == 1 || j == 1 || i == 6 || j == 6, 9, 0], {i, 1, 6}, {j,
     1, 6}];

color[v_] := 
  Switch[v, 0, Black, 1, Yellow, 2, Blue, 3, Magenta, 4, Cyan, 5, Red,
    6, Orange, 7, Green, 9, Gray, _, Gray];
showSquare[{i_, j_}, v_] := {color[v], 
   Rectangle[{i + 0.05, j + 0.05}, {i + 0.95, j + 0.95}]};
showHintBlock[n_, p_, x_, y_] := 
  Table[showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], {i, 1, 
    4}, {j, 1, 4}];


initBoard[
   Hold[board_, num_]] := {board = 
    Table[If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 9, 0], {i, 1, 
      16}, {j, 1, 28}], 
   Table[board[[i, 24]] = 0; board[[i, 25]] = 0; 
    board[[i, 26]] = 0, {i, 7, 10}]; 
   Table[board[[i, 25]] = 0, {i, 1, 16}]; board[[6, 25]] = 9, 
   board[[11, 25]] = 9,
   Table[If[Mod[j + i, 2] == 0, board[[j, i + 3]] = 9], {i, 1, 
     num}, {j, 4, 13}]};

bMoveTo[n_, p_, x_, y_, Hold[board_]] := 
  Total[Table[
     allBlocks[[n, p, i, j]]* board[[i + x, j + y]], {i, 1, 4}, {j, 1,
       4}], 2] == 0;
bGameOver[n_, p_, Hold[board_]] := 
  Not[bMoveTo[n, p, 6, 22, Hold[board]]];
bLeft[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x - 1, y, Hold[board]];
bRight[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x + 1, y, Hold[board]];
bDown[n_, p_, x_, y_, Hold[board_]] := 
  bMoveTo[n, p, x, y - 1, Hold[board]];
bRotateAnticlock[n_, p_, x_, y_, Hold[board_]] := 
 Module[{tp}, tp = p - 1; If[tp == 0, tp = 4]; 
  bMoveTo[n, tp, x, y, Hold[board]]]; 
bRotateClockwise[n_, p_, x_, y_, Hold[board_]] := 
 Module[{tp}, tp = p + 1; If[tp == 5, tp = 1]; 
  bMoveTo[n, tp, x, y, Hold[board]]];
showBoard[Hold[board_]] := 
  Table[showSquare[{i, j}, board[[i, j]]], {i, 3, 14}, {j, 3, 25}];
showDropBlock[n_, p_, x_, y_] := 
  Table[If[allBlocks[[n, p, i, j]] != 0, 
    showSquare[{i + x, j + y}, allBlocks[[n, p, i, j]]], Black], {i, 
    1, 4}, {j, 1, 4}];


updateBoard[n_, p_, x_, y_, 
   Hold[board_, score_]] := {Table[
    board[[i + x, j + y]] += allBlocks[[n, p, i, j]], {i, 1, 4}, {j, 
     1, 4}]; score += 
    10 + 100*(2^
         Sum[updateLine[i, Hold[board]], {i, y + 4, y + 1, -1}] - 
        1)};
updateLine[line_, Hold[board_]] := 
  If[line <= 23 && 
    line >= 4 && (Apply[And, 
       Table[board[[i, line]] != 0, {i, 4, 13}]]) == True, 
   Table[board[[i, j]] = board[[i, j + 1]], {i, 4, 13}, {j, line, 
     22}]; Table[board[[i, 23]] = 0, {i, 4, 13}]; 1, 0];
newGame[Hold[n_, p_, x_, y_, board_, nextItem_, gameOver_, pause_, 
    num_]] := {initBoard[
    Hold[board, num]]; {n, p, x, y} = {nextItem, 2, 6, 22}; 
   nextItem = RandomInteger[6] + 1; gameOver = False; pause = False};
newBlock[Hold[n_, p_, x_, y_, board_, score_, gameOver_, nextItem_, 
    pause_]] := {If[! pause && ! gameOver, 
    updateBoard[n, p, x, y, Hold[board, score]]; 
    gameOver = bGameOver[nextItem, 2, Hold[board]]; 
    If[gameOver == False, {n, p, x, y} = {nextItem, 2, 6, 22}; 
     nextItem = RandomInteger[6] + 1]]};


shapeDescend[Hold[level_, state_, num_]] := 
  DynamicModule[{score = 0, gameOver = False, pause = False, 
    board = Table[
      If[i <= 3 || j <= 3 || i >= 14 || j >= 24, 0, 0], {i, 1, 
       16}, {j, 1, 28}], n, p, x, y, nextItem}, 
   SeedRandom[level*10 + num]; nextItem = RandomInteger[6] + 1; 
   newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause, num]];
   EventHandler[
    Graphics[{Text[Style["Level", Blue, Italic, 24], {18, 12}], 
      Text[Style["Score", Blue, Italic, 24], {18, 9}], 
      Table[showSquare[{i + 16, j + 13}, smallBoard[[i, j]]], {i, 1, 
        6}, {j, 1, 6}], 
      Dynamic[Refresh[
        Switch[state, 1, 
         newGame[Hold[n, p, x, y, board, nextItem, gameOver, pause, 
           num]]; state = 3,
         2, pause = True,
         3, pause = False], UpdateInterval -> Infinity, 
        TrackedSymbols -> {state}]; 
       Refresh[If[! pause && ! gameOver && 
          bDown[n, p, x, y, Hold[board]], y--, 
         newBlock[
          Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
         UpdateInterval -> 3./level, TrackedSymbols -> {}];
       Join[{{Text[
           Style[ToString[level], Green, Italic, 24], {20, 11}], 
          Text[Style[ToString[score], Green, Italic, 24], {20, 8}]}}, 
        showBoard[Hold[board]], showDropBlock[n, p, x, y], 
        showHintBlock[nextItem, 2, 17, 14]], 
       TrackedSymbols -> {x, y, n, p, level}]}, Background -> Black, 
     ImageSize -> {400, 400}], {"LeftArrowKeyDown" :> 
      If[! pause && ! gameOver && bLeft[n, p, x, y, Hold[board]], x--],
     "RightArrowKeyDown" :> 
      If[! pause && ! gameOver && bRight[n, p, x, y, Hold[board]], 
       x++], "DownArrowKeyDown" :> (If[! pause && ! gameOver && 
         bRotateClockwise[n, p, x, y, Hold[board]], p++; 
        If[p == 5, p = 1]]), 
     "UpArrowKeyDown" :> (If[! pause && ! gameOver && 
         bRotateAnticlock[n, p, x, y, Hold[board]], p--; 
        If[p == 0, p = 4]]), {"KeyDown", " "} :> 
      If[! pause && ! gameOver && bDown[n, p, x, y, Hold[board]], y--,
        newBlock[
        Hold[n, p, x, y, board, score, gameOver, nextItem, pause]]],
     "EscapeKeyDown" :> {newGame[
        Hold[n, p, x, y, board, nextItem, gameOver, pause, num]]}}]];

Manipulate[
 shapeDescend[Hold[level, state, initLine]],
 {{level, 5}, 1, 9, 1},
 {{initLine, 3, "height of bottom level"}, 0, 9, 1},
 {{state, 3, ""}, {1 -> "new game", 2 -> "pause", 3 -> "continue"}},
 ContentSize -> {480, 450}, SynchronousUpdating -> False, 
 SaveDefinitions -> True, Alignment -> Center, TrackedSymbols -> {}]

4

С

Я написал это дааааара назад, когда сидел скучно в классе в старшей школе. Предположительно, оригинальный программист написал первую версию тетриса, используя квадратные скобки в качестве блоков, и это казалось достаточно простым, чтобы попытаться воссоздать, не так ли? Я не знал ничего о графическом интерфейсе, поэтому я сделал хорошую старомодную консольную программу. Это было еще до того, как я выучил C ++ и эти надоедливые методы программирования, поэтому код может быть немного грязным. Я в значительной степени только крылатый это.

TKlone

Он отвечает всем требованиям испытания, за исключением того, что фигура вращается только в одном направлении (по часовой стрелке). Используйте WASD для игры, W вращает фигуру. Полный исходный код и exe можно найти здесь: http://sourceforge.net/projects/tklone/files/tklone/tklone-v1.0/

tetris.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include "defines.h"

int array[FIELD_H][FIELD_W];    //field array
int pieceCoords[2];             //current piece coordinates
int x, y;                       //looping variables
int thisPiece, nextPiece;       //current pieces
double score;                   //the players current score
int newpiece=1;                 //the newpiece flag
int init_h=1;                   /*starting coordinates of piece*/
int init_w=4;                   /*these are obsolete variables*/
int h_offset, w_offset;         //piece position offsets from start
int rtTop, rtBot, rtLeft, rtRight;  //rotation thresholds for collision
int b1h, b1w, b2h, b2w, b3h, b3w;   //block draw offsets
int rotation;                   /*0=normal view
                                  1=90 degrees clockwise
                                  2=upside-down
                                  3=270 degrees clockwise */

clock_t speed;          //time before piece falls (in milliseconds)
clock_t gravity;        //temp variable for speed

//clock_t fps;
int piececount;         //number of pieces that have fallen
int oldpiececount;
int level;


void GameInit(void);
int GameStart(void);
int GameMain(void);
void newPiece(void);

void scrollbar(void);           //scrolling marquee bar
void drawField(void);           //graphics rendering function
void setPiece(int piece);       //this sets the individual piece offsets
void drawPiece(void);           //this sets the blocks in the array
void initArray(void);           //this initializes the array
void getMoves(char move);       //input function
void rotate(int coll);          //loops from 1 to 4
int collisionCheck(int side);   //checks to see if piece will collide
int loseGame(void);             //checks to see if game has ended
void eraseLastMove(void);       //clears the array of the last move
void setLastMove(void);         //sets the piece in place for the next piece
void lineClear(void);           //clears filled lines and tabulates the score
void predict(int undo);         //predicts the next piece rotation for collision
//void delay(int duration);

int main()
{
restart:
    GameInit();
    //some test blocks
    /*for(x=FIELD_H;x>3;x--)        //quick lose pattern
        array[x][4]=1;*/

    nextPiece=rand()%7;     //generate a piece to use
    drawField();            //draw a blank field. should be a title screen
    printf("                             / Welcome to TETRIS! \\"); //29 spaces and title

    getch();            //wait to start the game

    while(1)    //main game loop
    {
        if(newpiece==1)         //check to see if a new piece is required
            newPiece();

        //if(gravity<=clock())
            gravity=speed+clock();  //get the end time
        while(gravity>clock())      //count to the end time
        {
            if(kbhit())
            {
                getMoves(getch());  //get input
                if(newpiece==1)     //if piece reaches bottom...
                    break;          //...skip by redraw to save some cycles
                else
                {
                    eraseLastMove();    //clear the last move from the array
                    drawPiece();        //set the piece in the array
                    drawField();        //refresh the screen
                    scrollbar();        //draw the scrollbar
                }
            }
        }
        if(newpiece==0)     /*minor speed optimization*/
            getMoves('s');  //force piece down a line
        eraseLastMove();    //clear the last move from the array
        drawPiece();        //set the piece in the array
        drawField();        //refresh the screen
        scrollbar();        //draw the scrollbar
    }
    return 0;
}

void GameInit()
{
    srand((unsigned)time(NULL));    //seed the random number genreator...
    initArray();                    //...and initialize the array
    //reset some variables
    score=0;
    piececount=-1;      //set to -1 because of newpiece loop on start
    oldpiececount=0;
    speed=1000;         //starting speed of piece
    level=1;
}

void newPiece()
{
    piececount++;       //another piece has fallen
    setLastMove();      //set the last move in place
    lineClear();        //clear any full lines

    newpiece=0;         //reset the newpiece flag
    rotation=0;         //reset the piece rotation
    h_offset=0;         //reset the...
    w_offset=0;         //...piece coordinates
    gravity=0;          //reset the gravity

    if(piececount-oldpiececount==10)    //if 10 more pieces have fallen
        if(speed!=200)                  //200 is the lowest speed it can go
        {
            oldpiececount=piececount;   //set current number of fallen pieces
            speed-=100;                 //make the speed faster
            level++;                    //increment the level counter
        }

    thisPiece=nextPiece;//get the current piece
    nextPiece=rand()%7; //pick the next piece

    setPiece(thisPiece);//get piece data
    if(loseGame())      //did player lose the game?
    {
        drawField();    //refresh the screen
        printf("You lose! Final score: %.0f. Press any key to begin a new game.",score);
        getch();
        newpiece=1;     //reset the newpiece flag /*add more initialization here*/
        //goto restart; //return to start of the program
        exit(0);        //quit the game for now
    }
    drawPiece();        //set the piece in the array
    drawField();        //refresh the screen
    scrollbar();        //draw the scrollbar
}

void scrollbar()
{
    //printf("================================================================================");

    printf("Score: %.0f\t\t      ",score);  //print the score (6 spaces)
    if(nextPiece==5||nextPiece==6)          //print a space if the piece name is short
        printf(" ");

    printf("Next piece: ");
    if(nextPiece==0)            //print the name of the next piece
        printf("straight");
    else if(nextPiece==1)
        printf("t-shaped");
    else if(nextPiece==2)
        printf("s-shaped");
    else if(nextPiece==3)
        printf("z-shaped");
    else if(nextPiece==4)
        printf("l-shaped");
    else if(nextPiece==5)
        printf("back-l");
    else if(nextPiece==6)
        printf("square");

    printf("\t\t\tLevel %i",level); //print the level
}

void drawField()
{
    system("cls");      //clear the screen

    for(x=0;x<FIELD_H;x++)
    {
        printf("                              |");  //thirty spaces

        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==1||array[x][y]==2)
                printf("[]");
            else
                printf("  ");

        printf("|                              ");  //thirty spaces
    }

    //bottom message bar
    //scrollbar();
}

void drawPiece()
{
    /* This sets the initial location of the piece with
       movement offset. Coordinates are based on the
       center of the piece. */
    pieceCoords[0]=init_h+h_offset; //set height position
    pieceCoords[1]=init_w+w_offset; //set width position

    //mark the coordinates to draw the piece
    array[(pieceCoords[0])][(pieceCoords[1])]=2;        //center piece
    array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]=2;    //other...
    array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]=2;    //...three...
    array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]=2;    //...pieces
}

void initArray()
{
    for(x=0;x<FIELD_H;x++)
        for(y=0;y<FIELD_W;y++)
            array[x][y]=0;
        //array equals: 0 for empty
        //              1 for full
        //              2 for moving piece
}

void getMoves(char move)
{
    switch(move)
    {
        case 'a':
        case 'A':
            if(collisionCheck(1)==0)    //if the piece doesnt hit something...
                w_offset--;             //...move the piece
            break;
        case 'd':
        case 'D':
            if(collisionCheck(2)==0)
                w_offset++;
            break;
        case 'w':
        case 'W':
            if(collisionCheck(0)==0)
            {
                rotate(0);              //rotate the piece and...
                setPiece(thisPiece);    //...get new piece data
            }
            break;
        case 's':
        case 'S':
            if(collisionCheck(3)==0)
                h_offset++;
            else                        //if the piece hits the bottom...
                newpiece=1;             //...set the newpiece flag
            break;
        default:
            break;
    }
}

void rotate(int coll)
{
    if(coll==0)     //rotate clockwise
    {
        if(rotation==3)
            rotation=0;
        else
            rotation++;
    }

    if(coll==1)     //rotate counterclockwise
    {
        if(rotation==0)
            rotation=3;
        else
            rotation--;
    }
}

int collisionCheck(int side)
{
    if(side==0) //check rotation collision
    {
        predict(0);

        /* For the first part, we check to see if the
           rotation will collide with the sides. */
        for(x=0;x<rtTop;x++)    //check top side collision
        {
            if(pieceCoords[0]-x==0) //if piece minus top offset hits the top
            {
                predict(1);
                return 1;   //collision detected!
            }
        }

        for(x=0;x<rtLeft;x++)   //check left side collision
        {
            if(pieceCoords[1]-x==0)
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtRight;x++)  //check right side collision
        {
            if(pieceCoords[1]+x==(FIELD_W-1))
            {
                predict(1);
                return 1;
            }
        }

        for(x=0;x<rtBot;x++)    //check bottom side collision
        {
            if(pieceCoords[0]+x==(FIELD_H-1))
            {
                predict(1);
                return 1;
            }
        }

        /* Then we check to see if any of the
           blocks will hit other blocks on the field. */
        if(array[(pieceCoords[0]+b1h)][(pieceCoords[1]+b1w)]==1
    ||array[(pieceCoords[0]+b2h)][(pieceCoords[1]+b2w)]==1
    ||array[(pieceCoords[0]+b3h)][(pieceCoords[1]+b3w)]==1)
        {
            predict(1);
            return 1;
        }

        predict(1);
        return 0;
    }

    /* Here, we scan the array and check to see whether
       any of the blocks in the current piece are
       adjacent to the field sides or another block. */
    for(x=0;x<FIELD_H;x++)          //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if array location contains a block
            {
                if(side==1) //check left side
                {
                    if(y==0||array[x][y-1]==1)  //if piece is next to border or block...
                        return 1;   //collision detected!
                }

                else if(side==2)    //check right side
                {
                    if(y==FIELD_W-1||array[x][y+1]==1)
                        return 1;
                }

                else if(side==3)    //check bottom
                {
                    if(x==FIELD_H-1||array[x+1][y]==1)
                        return 1;
                }
            }

    return 0;
}

int loseGame()      /*need to modify the conditions of losing*/
{
    if(array[init_h][init_w]==1
||array[(init_h+b1h)][(init_w+b1w)]==1
||array[(init_h+b2h)][(init_w+b2w)]==1
||array[(init_h+b3h)][(init_w+b3w)]==1)
        return 1;

    else
        return 0;
}

void eraseLastMove()    /*need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=0;      //...clear the block
}

void setLastMove()      /*also need to optimize this*/
{
    for(x=0;x<FIELD_H;x++)      //scan the array
        for(y=0;y<FIELD_W;y++)
            if(array[x][y]==2)      //if the spot is filled...
                array[x][y]=1;      //...set the block
}

void lineClear()
{
    int x2, y2; //new looping variables
    int empty;  //is the row empty?
    int lines=0;//number of lines cleared

    for(x=0;x<FIELD_H;x++)      //scan the rows
    {
        empty=0;                //reset the empty flag on each row

        for(y=0;y<FIELD_W;y++)  //scan the blocks in each row
            if(array[x][y]==0)  //if the row isn't full...
            {
                empty=1;        //...set the empty flag and...
                break;          //...scan the next row
            }

        if(!empty)
        {
            for(y=0;y<FIELD_W;y++)      //rescan the filled row...
                array[x][y]=0;          //...and clear the blocks

            for(x2=x-1;x2>=0;x2--)      //scan rows from the bottom up starting above cleared row
                for(y2=0;y2<FIELD_W;y2++)   //scan each block in the row
                    if(array[x2][y2]==1)
                    {
                        array[x2][y2]=0;    //clear the block
                        array[x2+1][y2]=1;  //fill in the block below it
                    }

            lines++;            //a line has been cleared
        }
    }

    //tabulate the scores
    if(lines==1)
        score+=10;
    else if(lines==2)
        score+=25;
    else if(lines==3)
        score+=50;
    else if(lines==4)   /* TETRIS! */
        score+=100;
}

void predict(int undo)
{
    if(undo==0)
    {
        rotate(0);              //get next rotation...
        setPiece(thisPiece);    //...and next piece data
    }

    if(undo==1)
    {
        rotate(1);              //un-rotate
        setPiece(thisPiece);    //reset the piece
    }
}

/*void delay(int duration)
{
    clock_t done;

    done = duration + clock();
    while(done>clock())
        ;   //sit and wait
}*/

setPiece.c

#include "defines.h"

extern int rtTop, rtBot, rtLeft, rtRight;   //rotation thresholds for collision
extern int b1h, b1w, b2h, b2w, b3h, b3w;    //block draw offsets
extern int rotation;                    /*0=normal view
                                          1=90 degrees clockwise
                                          2=upside-down
                                          3=270 degrees clockwise */

void setPiece(int piece)
{
    if(piece==0)
    {
        switch(rotation)
        {
            case 0:     //piece looks identical rotated upside-down
            case 2:
                /* The rotation thresholds dictate how far away the piece
                   needs to be from the field edge for the next rotation. */
                rtTop=1;
                rtBot=2;
                rtLeft=0;
                rtRight=0;

                /* These are the coordinates of the
                   blocks relative to the center block. */
                b1h=-1;
                b1w=0;
                b2h=1;
                b2w=0;
                b3h=2;
                b3w=0;
                break;
            case 1:     //piece looks identical side to side
            case 3:
                rtTop=0;
                rtBot=0;
                rtLeft=1;
                rtRight=2;

                b1h=0;
                b1w=-1;
                b2h=0;
                b2w=1;
                b3h=0;
                b3w=2;
                break;
        }
    }
        //draw blue straight piece

    //snip other pieces

    else if(piece==6)
    {
        switch(rotation)
        {
            case 0:     //a cube looks identical when rotated 90 degrees
            case 1:
            case 2:
            case 3:
                rtTop=0;    //piece doesn't rotate so no thresholds needed
                rtBot=0;
                rtLeft=0;
                rtRight=0;

                b1h=-1;
                b1w=0;
                b2h=-1;
                b2w=1;
                b3h=0;
                b3w=1;
                break;
        }
    }
        //draw square piece
}

Здравствуй! Добро пожаловать в PPCG. Мы настоятельно рекомендуем ответам включать их исходный код в текст ответа, если только он не слишком велик для максимального размера сообщения. Даже тогда полезно привести некоторые иллюстративные выдержки.
Джонатан Ван Матре

2
Код добавлен. setPiece.c был немного сокращен и определен .h нет в списке. Оригинальный код можно найти по ссылке.
Bigbio2002

1
Потрясающе! Спасибо, что поделились этим.
Джонатан Ван Матре

1

Я создал версию JavaScript https://marchingband.github.io/tetris/

введите описание изображения здесь

<!DOCTYPE html>
<html>
  <body>
      <p>q and w rotate</p>
      <p id='score'>0</p>
      <div id='board' style='position:absolute;top:80px' ></div>
  </body>
</html>
  <script>
    var pieceIndex=0
    nextPiece=()=>{
      pieceIndex=pieceIndex<pieces.length-1?pieceIndex+1:0
      return pieces[pieceIndex]
    }
    const pieces=[
    [
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      [
        {x:0,y:-2},
        {x:0,y:-1},
        {x:0,y:0},
        {x:0,y:1},
      ],
      [
        {x:1,y:0},
        {x:0,y:0},
        {x:2,y:0},
        {x:0,y:-1},
      ],
      [
        {x:0,y:0},
        {x:1,y:0},
        {x:1,y:1},
        {x:0,y:1}
      ],
      [
        {x:0,y:0},
        {x:-1,y:0},
        {x:0,y:1},
        {x:1,y:1}
      ],
    ]
    const speeds =[1000,800,600,400,300,250,200,150,100,90,80,70,60,50,40,30,20]
    const boardWidth = 10
    const boardHeight = 20
    const pixelSize   = 10
    const pixelBorder = 0
    const boardBorder = 5
    
    var score = 0
    var level = 0
    var fallingBits = []

    const b = document.getElementById('board')
     piece = {
      position:{
        x:boardWidth/2,
        y:1,
      },
      bits:[
        {x:0,y:-1},
        {x:0,y:0},
        {x:1,y:0},
        {x:-1,y:0},
      ],
      reset(){
        piece.position={x:boardWidth/2,y:1}
        piece.bits=nextPiece()
        !piece.coordinates().every((b)=>dom[b.x][b.y]?dom[b.x][b.y].style.background=='red':true) && endGame()
      },
      coordinates(){
        return piece.bits.map((b)=>{
          return {x:b.x+piece.position.x,y:b.y+piece.position.y}
        })
      }
    }
    const dom = []
    for(var x=0;x<boardWidth;x++){
      var row = []
      for(var y=0;y<boardHeight;y++){
        var cell = document.createElement('div')
        cell.style.cssText='outline:thin solid;height:'+pixelSize+'px;width:'+pixelSize+'px;background:red;position:absolute'
        cell.style.left= (x*(pixelSize+pixelBorder)+boardBorder) + 'px'
        cell.style.top = (y*(pixelSize+pixelBorder)+boardBorder) + 'px'
        row.push(cell)
        b.appendChild(cell)
      }
      dom.push(row)
    }
    renderPiece=(color)=>{
      piece.coordinates().forEach((b)=>dom[b.x][b.y] && (dom[b.x][b.y].style.background=color))
    }
    drop=()=>{
      piece.coordinates().every((p)=>p.y+1<boardHeight) && piece.coordinates().every((p)=>dom[p.x][p.y+1].style.background!='blue') ?
        movePiece(0,1) : freeze()
    }
    strafe=(d)=>{
      piece.coordinates().every((p)=>p.x+d<boardWidth && p.x+d >=0) && piece.coordinates().every((p)=>dom[p.x+d][p.y].style.background!='blue') ?
        movePiece(d,0) : null
    }
    freeze=()=>{
      piece.coordinates().forEach((p)=>dom[p.x][p.y].style.background='blue')
      lookForRows()
      piece.reset()
    }
    lookForRows=()=>{
      [...dom[0].keys()].filter((i)=>dom.every((c)=>c[i].style.background=='blue')).forEach((r)=>{
        removeLine(r)
        compressAbove(r)
        addPoint()
      })
    }
    compressAbove=(r)=>{
      // dom.reduce((acc,col,x)=>{return acc.concat(col.map((b,y)=>{return {x:x,y:y}}).filter((b,y)=>y<r && dom[b.x][b.y].style.background=='blue'))},[]).forEach((b)=>dom[b.x][b.y].style.background='red').forEach((b)=>dom[b.x][b.y+1].style.background='blue')
      var bits = []
      dom.forEach((x,i)=>{
        x.forEach((y,j)=>{
          if(j<=r && dom[i][j].style.background=='blue'){bits.push({x:i,y:j})}
        })
      })
      bits.forEach((b)=>{
        dom[b.x][b.y].style.background='red'
      })
      bits.forEach((b)=>{
        dom[b.x][b.y+1].style.background='blue'
      })
    }
    removeLine=(r)=>{
      dom.forEach((x)=>x[r].style.background='red')
    }
    changePiece=(x,y)=>{
      piece.position.x += x
      piece.position.y += y
    }
    movePiece=(x,y)=>{
        renderPiece('red')
        changePiece(x,y)
        renderPiece('yellow')
    }
    turn=(d)=>{
      var newBits = []
      for(let i=0;i<piece.bits.length;i++){
        var x = piece.bits[i].x
        var y = piece.bits[i].y
        var bit={}
        if(d==1){
          bit.x=-y
          bit.y=x
        }else{
          bit.x=y
          bit.y=-x
        }
        newBits.push(bit)
      }
      if(newBits.every((p)=>p.x+piece.position.x<boardWidth && p.x+piece.position.x>=0 && p.y+piece.position.y<boardHeight && dom[p.x+piece.position.x][p.y+piece.position.y].style.background!='blue')){
        renderPiece('red')
        piece.bits=newBits
        renderPiece('yellow')
      }
    }
    addPoint=()=>{
      score++
      document.getElementById('score').innerText=score
      score-level*10 > 10 && levelUp()
    }
    levelUp=()=>{
      clearInterval(timer)
      level++
      timer = window.setInterval(drop,speeds[level])
    }
    endGame=()=>{
      clearInterval(timer)
      dom.forEach((row)=>{
        row.forEach((bit)=>{
          bit.style.background=='red' && (bit.style.background='black')
        })
      })
    }
    document.onkeydown = function(e) {
    switch (e.keyCode) {
        case 37:strafe(-1);break
        case 87:turn(1);break
        case 81:turn(-1);break
        case 39:strafe(1);break
        case 40:drop();break
    }
};
  var timer = window.setInterval(drop,1000)
  </script>
</html>


Хороший. К сожалению, есть небольшая ошибка в Firefox, когда все фигуры падают до дна ямы, потому что dom[p.x][p.y+1].style.backgroundвозвращаются blue none repeat scroll 0% 0%, поэтому никогда не будут равны blue. Ваше решение действительно, но я бы посоветовал изменить все .style.background.style.backgroundColorдля нормальной работы как в Firefox, так и в Chrome.
Манатворк
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.