Недавно мне в голову пришла идея — написать простенькую игру в минимальное количество строк. Мой выбор пал на тетрис. В этой статье я опишу свой код.

Для начала стоит отметить, что в свою реализацию я включил только базовые возможности:

  • движение фигурок влево/вправо;
  • падение фигурок;
  • поворот фигурок;
  • удаление заполненных фигурок;
  • окончание игры.

Итак, сперва добавим на форму PictureBox и создадим таймер.

Также для игры понадобятся:

        public const int width = 15, height = 25, k = 15; // Размеры поля и размер клетки в пикселях
        public int[,] shape = new int[2, 4]; // Массив для хранения падающей фигурки (для каждого блока 2 координаты [0, i] и [1, i]
        public int[,] field = new int[width, height]; // Массив для хранения поля
        public Bitmap bitfield = new Bitmap(k * (width + 1) + 1, k * (height + 3) + 1);
        public Graphics gr; // Для рисования поля на PictureBox

Заполним поле по краям:

            for (int i = 0; i < width; i++)
                field[i, height - 1] = 1;
            for (int i = 0; i < height; i++) {
                field[0, i] = 1;
                field[width - 1, i] = 1;
            }

Заполним фигуру:

       public void SetShape(){
            Random x = new Random(DateTime.Now.Millisecond);
            switch (x.Next(7)){ // Рандомно выбираем 1 из 7 возможных фигурок
                case 0: shape = new int[,] { { 2, 3, 4, 5 }, { 8, 8, 8, 8 } }; break; 
                case 1: shape = new int[,] { { 2, 3, 2, 3 }, { 8, 8, 9, 9 } }; break;
                case 2: shape = new int[,] { { 2, 3, 4, 4 }, { 8, 8, 8, 9 } }; break;
                case 3: shape = new int[,] { { 2, 3, 4, 4 }, { 8, 8, 8, 7 } }; break;
                case 4: shape = new int[,] { { 3, 3, 4, 4 }, { 7, 8, 8, 9 } }; break;
                case 5: shape = new int[,] { { 3, 3, 4, 4 }, { 9, 8, 8, 7 } }; break;
                case 6: shape = new int[,] { { 3, 4, 4, 4 }, { 8, 7, 8, 9 } }; break;
            }
        }

Процедура, которая рисует «стакан» в PictureBox:

public void FillField(){
            gr.Clear(Color.Black); //Очистим поле
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                    if (field[i, j] == 1){ // Если клетка поля существует
                        gr.FillRectangle(Brushes.Green, i * k, j * k, k, k); // Рисуем в этом месте квадратик
                        gr.DrawRectangle(Pens.Black, i * k, j * k, k, k);
            }
            for (int i = 0; i < 4; i++){ // Рисуем падающую фигуру
                gr.FillRectangle(Brushes.Red, shape[1, i] * k, shape[0, i] * k, k, k);
                gr.DrawRectangle(Pens.Black, shape[1, i] * k, shape[0, i] * k, k, k);
            }
            FieldPictureBox.Image = bitfield;
        }

Фигурки я сначала двигаю, а потом проверяю, возможен такой вариант или нет. Если в каком-то месте находится ошибка (фигура вышла за пределы поля или наложилась на уже лежащую в поле фигуру), фигурка возвращается на своё прежнее место. Для этого я написал функцию, которая возвращает true, если на поле нашлась ошибка или false, если ошибки нет:

       public bool FindMistake(){
            for (int i = 0; i < 4; i++)
                if (shape[1, i] >= width || shape[0, i] >= height || 
                    shape[1, i] <= 0 || shape[0, i] <= 0 || 
                    field[shape[1, i], shape[0, i]] == 1)
                        return true; 
            return false;
        }

Теперь приступим к передвижению фигурки. Создадим в конструкторе событие KeyDown.

Движение влево:

      switch (e.KeyCode){
            case Keys.A:
                for (int i = 0; i < 4; i++) 
                    shape[1, i]--; // Сначала сдвигаем координаты всех кусочков фигуры на 1 влево по оси OX 
                if (FindMistake()) // Если после этого нашлась ошибка
                    for (int i = 0; i < 4; i++) 
                            shape[1, i]++; // Возвращаем фигурку обратно на 1 вправо
                break;
            ...
           }

Аналогично движение вправо:

 case Keys.D:
     for (int i = 0; i < 4; i++) 
          shape[1, i]++;
     if (FindMistake())
          for (int i = 0; i < 4; i++)
               shape[1, i]--;
break;

Переворот фигурки устроен немного сложнее:

case Keys.W:               
                var shapeT = new int[2, 4]; 
                Array.Copy(shape, shapeT, shape.Length); // Создадим копию фигурки, чтобы в случае, когда после переворота на поле найдется ошибка, не переворачивать её обратно, а просто восстановить копию
                int maxx = 0, maxy = 0; 
                for (int i = 0; i < 4; i++){
                    if (shape[0, i] > maxy)
                            maxy = shape[0, i]; 
                    if (shape[1, i] > maxx)
                            maxx = shape[1, i];
                } // Найдем максимальные координаты значения фигуры по X и по Y
                for (int i = 0; i < 4; i++) { 
                    int temp = shape[0, i];
                    shape[0, i] = maxy - (maxx - shape[1, i]) - 1;
                    shape[1, i] = maxx - (3 - (maxy - temp)) + 1;
                } // Перевернем фигуру. Эти формулы я обнаружил после тщательного изучения нарисованного квадратика с фигурой до и после переворота.
                if (FindMistake())
                        Array.Copy(shapeT, shape, shape.Length);                
                break;

Теперь осталось только добавить падение фигурок и убирание строк. Все это будет происходить по событию TimerTick:

private void TickTimer_Tick(object sender, System.EventArgs e){
            if (field[8, 3] == 1)
                Environment.Exit(0); // Если клетка поля, на которой появляются фигурки заполнены, завершить программу.
            for (int i = 0; i < 4; i++) 
                shape[0, i]++; // Сместить фигурку вниз
            if (FindMistake()){
                for (int i = 0; i < 4; i++)
                    field[shape[1, i], --shape[0, i]]++;                 
                SetShape();
            } // Если нашлась ошибка, перенести фигурку на 1 клетку вверх, сохранить её в массив field и создать новую фигурку
            for (int i = height - 2; i > 2; i--){
                var cross = (from t in Enumerable.Range(0, field.GetLength(0)).Select(j => field[j, i]).ToArray() where t == 1 select t).Count(); // Количество заполненных полей в ряду
                if (cross == width)
                    for (int k = i; k > 1; k--)
                        for (int l = 1; l < width - 1; l++)
                            field[l, k] = field[l, k - 1];
            } // Проверка на заполненность рядом, если нашлись ряды, в которых все клетки заполнены, сместить все ряды, которые находятся выше убранной линии, на 1 вниз

            FillField(); // Перерисовать поле
        }

Ну и наконец, при создании формы надо вызвать:

   SetShape();

Итоговый код:

using System;
using System.Linq;
using System.Drawing;
using System.Windows.Forms;
namespace LittleTetris{
    public partial class Form1 : Form{
        public const int width = 15, height = 25, k = 15;
        public int[,] shape = new int[2, 4];
        public int[,] field = new int[width, height];
        public Bitmap bitfield = new Bitmap(k * (width + 1) + 1, k * (height + 3) + 1);
        public Graphics gr;
        public Form1(){
            InitializeComponent();
            gr = Graphics.FromImage(bitfield);
            for (int i = 0; i < width; i++)
                field[i, height - 1] = 1;
            for (int i = 0; i < height; i++) {
                field[0, i] = 1;
                field[width - 1, i] = 1;
            }
            SetShape();
        }
        public void FillField(){
            gr.Clear(Color.Black);
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                    if (field[i, j] == 1){
                        gr.FillRectangle(Brushes.Green, i * k, j * k, k, k);
                        gr.DrawRectangle(Pens.Black, i * k, j * k, k, k);
                    }
            for (int i = 0; i < 4; i++){
                gr.FillRectangle(Brushes.Red, shape[1, i] * k, shape[0, i] * k, k, k);
                gr.DrawRectangle(Pens.Black, shape[1, i] * k, shape[0, i] * k, k, k);
            }
            FieldPictureBox.Image = bitfield;
        }
        private void TickTimer_Tick(object sender, System.EventArgs e){
            if (field[8, 3] == 1)
                Environment.Exit(0);
            for (int i = 0; i < 4; i++) 
                shape[0, i]++;
            for (int i = height - 2; i > 2; i--){
                var cross = (from t in Enumerable.Range(0, field.GetLength(0)).Select(j => field[j, i]).ToArray() where t == 1 select t).Count();
                if (cross == width)
                    for (int k = i; k > 1; k--)
                        for (int l = 1; l < width - 1; l++)
                            field[l, k] = field[l, k - 1];}
            if (FindMistake()){
                for (int i = 0; i < 4; i++)
                    field[shape[1, i], --shape[0, i]]++;                 
                SetShape();}
            FillField();
        }
        private void Form1_KeyDown(object sender, KeyEventArgs e){
           switch (e.KeyCode){
            case Keys.A:
                for (int i = 0; i < 4; i++)
                    shape[1, i]--;
                if (FindMistake())
                    for (int i = 0; i < 4; i++)
                            shape[1, i]++;
                break;
            case Keys.D:
                for (int i = 0; i < 4; i++)
                        shape[1, i]++;
                if (FindMistake())
                        for (int i = 0; i < 4; i++)
                            shape[1, i]--;
                break; 
            case Keys.W:               
                var shapeT = new int[2, 4];
                Array.Copy(shape, shapeT, shape.Length);
                int maxx = 0, maxy = 0; 
                for (int i = 0; i < 4; i++){
                    if (shape[0, i] > maxy)
                            maxy = shape[0, i]; 
                    if (shape[1, i] > maxx)
                            maxx = shape[1, i];
                }
                for (int i = 0; i < 4; i++) { 
                    int temp = shape[0, i];
                    shape[0, i] = maxy - (maxx - shape[1, i]) - 1;
                    shape[1, i] = maxx - (3 - (maxy - temp)) + 1;
                }
                if (FindMistake())
                    Array.Copy(shapeT, shape, shape.Length);
                break;      
           }
        }
        public void SetShape(){
            Random x = new Random(DateTime.Now.Millisecond);
            switch (x.Next(7)){
                case 0: shape = new int[,] { { 2, 3, 4, 5 }, { 8, 8, 8, 8 } }; break;
                case 1: shape = new int[,] { { 2, 3, 2, 3 }, { 8, 8, 9, 9 } }; break;
                case 2: shape = new int[,] { { 2, 3, 4, 4 }, { 8, 8, 8, 9 } }; break;
                case 3: shape = new int[,] { { 2, 3, 4, 4 }, { 8, 8, 8, 7 } }; break;
                case 4: shape = new int[,] { { 3, 3, 4, 4 }, { 7, 8, 8, 9 } }; break;
                case 5: shape = new int[,] { { 3, 3, 4, 4 }, { 9, 8, 8, 7 } }; break;
                case 6: shape = new int[,] { { 3, 4, 4, 4 }, { 8, 7, 8, 9 } }; break;
            }
        }
        public bool FindMistake(){
            for (int i = 0; i < 4; i++)
                if (shape[1, i] >= width || shape[0, i] >= height || 
                    shape[1, i] <= 0 || shape[0, i] <= 0 || 
                    field[shape[1, i], shape[0, i]] == 1)
                        return true; 
            return false;
        }
    }
}

В итоге, если не считать строк с закрывающейся скобкой и расписанного условия в FindMistake, получается 95 строк.

Вот скриншот работающей программы:

image

Комментарии (36)


  1. megaboich
    21.12.2018 16:24
    -8

    В 2018 году такие статьи не могут вызвать большой интерес для широкой аудитории.
    Неужели кто-то еще использует WinForms?


    1. kekekeks
      21.12.2018 16:32
      +14

      Их и в 2038 будут использовать, технология в освоении проста аки грабли и со своими задачами прекрасно справляется.


      1. Oplkill
        22.12.2018 22:48

        Плюс теперь он стал opensource, так что умрёт ещё не скоро


    1. baimkin
      21.12.2018 16:45
      +3

      Я использую и думаю не только я, для enterprise систем самое то.
      В WinForms максимум функционала на единицу площади экрана и привычный для пользователей графический интерфейс.


  1. FadeToBlack
    21.12.2018 17:00

    помнится, я на спор писал тетрис за час… найду ли… Нашел. Даже два.(фигуры). А на самом деле их было не меньше 5.


    1. FadeToBlack
      22.12.2018 07:47

      И что не так с моими тетрисами? Первый я писал на первом курсе, второй как пример для моего 3д движка.


  1. Oxoron
    21.12.2018 17:03

    Можно сократить:

    case Keys.A: 
        for (int i = 0; i < 4; i++)
            shape[1, i]--;
        if (FindMistake())
            for (int i = 0; i < 4; i++)
                shape[1, i]++;
    break;
    case Keys.D:
        for (int i = 0; i < 4; i++)
            shape[1, i]++;
        if (FindMistake())
            for (int i = 0; i < 4; i++)
        shape[1, i]--;
    break; 


    до

    case Keys.A: Keys.D: 
        for (int i = 0; i < 4; i++)
            shape[1, i] = e.KeyCode == Keys.A ? shape[1, i]-1 : shape[1, i]+1;
        if (FindMistake())
            for (int i = 0; i < 4; i++)
                shape[1, i] = e.KeyCode == Keys.A ? shape[1, i]+1 : shape[1, i]-1;
    break;


    Upd1. Пофиксил синтакс.


    1. BkmzSpb
      21.12.2018 19:29

      Как насчет


      ...
      shape[1, i] += e.KeyCode == Keys.A ? -1 : 1;
      ...


      1. Oxoron
        21.12.2018 21:06

        Так короче, грац! Увы, на количество строк не влияет.


        1. FadeToBlack
          22.12.2018 14:47

          Короче — лучше. количество строк не главное. давайте по-честному, считать символы. а то можно так все в одну строку записать )


          1. Oxoron
            23.12.2018 14:50

            «Все-в-одну-строку» — слишком тривиально, чтоб об этом дискутировать. «До этого любой ламер додумается». Уменьшение же «честных» строк кода уже может привести к чему-то интересному

            Кстати, вопрос знатокам: можно ли написать отображение, переводящее индекс фигуры в фигуру (0 -> { 2, 3, 4, 5 }, { 8, 8, 8, 8 } )? С таким отображением получится выкинуть еще 8 строк.


      1. FadeToBlack
        22.12.2018 14:51

        а почему не сделать по-нормальному — функцию bool move(dx, dy)? она же еще в куче мест пригодится, например, движение вниз, движение обратно итд.


    1. Limpich Автор
      22.12.2018 10:40

      Спасибо за предложенное сокращение! На самом деле, это далеко не единственный вариант уменьшения кода. Можно убрать еще пару десятков строк. Но это, в основном, благодаря использованию LINQ. Например, заполненные строки в поле можно найти вообще в один запрос:

      (from i in Enumerable.Range(0, field.GetLength(1)) where (Enumerable.Range(0, field.GetLength(0)).Select(j => field[j, i]).Sum() >= width - 1) select i).ToArray().Take(1) 
      


      1. withkittens
        22.12.2018 18:37

        Можно, но зачем?


      1. AlienJust
        22.12.2018 19:23

        Кстати, не стоит забывать про строки связанные с дизайнером формы.

        ...

        public Graphics gr;
        private PictureBox FieldPictureBox { get; }
        private Timer TickTimer { get; }
        [STAThread]
        public static void Main() {
        var frm = new Form1();
        frm.ShowDialog();
        }

        private Form1() {
        KeyDown += Form1_KeyDown;
        FieldPictureBox = new PictureBox {Dock = DockStyle.Fill, Parent = this};
        TickTimer = new Timer {Interval = 1000, Enabled = true};
        TickTimer.Tick += TickTimer_Tick;

        ...


  1. geekmetwice
    21.12.2018 17:55
    +4

    Всё замечательно, фигуры падают, тетрис тетрисится. ЧТО ДАЛЬШЕ? К чему вся эта статья, а особенно замечание «в 100 строк»? Неужели где-то есть программисты, которые гордятся самым коротким кодом, будто они каждый символ выжигают у себя на коже??
    Статья и не обучающая, и не познавательная… тупо пришёл мальчик «я написал тетрис». Ну… как бы молодец, иди домой! :) Что вы хотите от читателей-то?


    1. Oxoron
      21.12.2018 18:22
      +1

      Показать всю ущербность C# по сравнению с JS где все угодно вмещается в 30 строк?


      1. Limpich Автор
        22.12.2018 10:31

        Ущербность языка здесь абсолютно не причем. Всё дело в реализации. Как пример, в коде на JS все состояния фигурок хранятся в одной строке. Я же счёл, что это нанесёт непоправимый вред читаемости кода и честно крутил фигурки)


        1. Oxoron
          23.12.2018 14:28
          +1

          Друг, это местный фольклор. Пару лет назад на хабре была серия «Х в 30 строк на JS». «Победителем» вышел товарищ, ужавший игру в 0 строк.


      1. balsoft
        22.12.2018 16:30

        Хрен редьки не слаще.


    1. FadeToBlack
      21.12.2018 18:42
      +1

      В каком-то смысле, чем короче код — тем лучше. Меньше лишнего, меньше ошибок, проще читать, быстрее править.


      1. FadeToBlack
        22.12.2018 08:05

        А можно мне понять ход мыслей минсующих? Я вот давно уже программирую, но может чего не знаю?


        1. Vindicar
          22.12.2018 08:49
          +1

          Ну про печально известную «программу из одной строки» вы наверняка слышали. Можно её назвать читаемой?
          Начиная с определенного момента, приемы, направленные на сокращение длины кода, начинают ухудшать читаемость.
          А еще не забываем про закон Парето применительно к программированию — «80% кода занимает обработка ошибок». Поэтому короткий код может означать «надеемся, что все получится».

          P.S.: не минусовал.


          1. FadeToBlack
            22.12.2018 14:45

            Ну я не в ответе за тот, код, который написан в одну строчку. Я говорил «код короче», а не меньше строчек. Понято, что код можно сокращать сколь угодно, абстрагируя логику все сильнее и сильнее. Вот, скажем, в обсуждении выше люди начали оптимизировать размер кода, выкидывая из него повторяющиеся куски. Например, есть же Linq — там можно написать одну строку, простую и понятную. Если вы начинаете ее расписывать — у вас получается пара экранов кода. Пока вы въедете, что он делает — пройдет минут пять, а то и больше, а строка linq прочитается и поймется за полминуты, хотя есть и такие извращенцы, которые пишут linq запросы в несколько строчек. Это ведь тоже можно сделать проще — например, не переносить на следующую строку, а сохранить часть выражения в переменную, дав ей осознанное имя. Такой код будет читать проще. Я не призываю сокращать код до нечитаемого мяса, я лишь говорю о том, что много кода писать — плохо. Когда вы начинаете задумываться об этом, вы не будете писать каждую вторую строчку, а в масштабах проекта это отодвинет неизбежную потерю контроля над кодом, снизит количество багов, и упростит вам жизнь.


            1. Oxoron
              23.12.2018 14:39

              хотя есть и такие извращенцы, которые пишут linq запросы в несколько строчек
              Ну зачем же вы так. LINQ в несколько строчек может быть вполне себе читаемым. Как раз тот случай, когда можно добавлять строки кода ради читаемости.
              var result = source.Select(dto => dto.ToModel())
                  .Where(model =>  model.IsActive)  // 2 where for a simpler debug
                  .Where(model => model.MatchesSecurityPolicy())
                  .GroupBy(model => model.businessGroupKey)
                  .ToDictionary(
                      group => groupKey, 
                      group => group.Sum(elem => elem.Karma));
              


  1. forcewake
    21.12.2018 18:32
    +2

    Можно и в 32 строчки вместиться:


    Но зачем?
    using System;using System.Linq;using System.Drawing;using System.Windows.Forms;
    public partial class Form1:Form{public const int width=15,height=25,k=15;public
    int[,]shape=new int[2,4];public int[,]field=new int[width,height];public Bitmap
    bitfield=new Bitmap(k*(width+1)+1,k*(height+3)+1);public Graphics gr;public
    Form1(){InitializeComponent();gr=Graphics.FromImage(bitfield);for(int a=0;a<
    width;a++)field[a,height-1]=1;for(int a=0;a<height;a++){field[0,a]=1;field[width
    -1,a]=1;}SetShape();}public void FillField(){gr.Clear(Color.Black);for(int a=0;a
    <width;a++)for(int b=0;b<height;b++)if(field[a,b]==1){gr.FillRectangle(Brushes.
    Green,a*k,b*k,k,k);gr.DrawRectangle(Pens.Black,a*k,b*k,k,k);}for(int a=0;a<4;a++
    ){gr.FillRectangle(Brushes.Red,shape[1,a]*k,shape[0,a]*k,k,k);gr.DrawRectangle(
    Pens.Black,shape[1,a]*k,shape[0,a]*k,k,k);}FieldPictureBox.Image=bitfield;}void
    h(object a,System.EventArgs b){if(field[8,3]==1)Environment.Exit(0);for(int c=0;
    c<4;c++)shape[0,c]++;for(int c=height-2;c>2;c--){int d=(from t in Enumerable.
    Range(0,field.GetLength(0)).Select(e=>field[e,c]).ToArray()where t==1 select t).
    Count();if(d==width)for(int f=c;f>1;f--)for(int g=1;g<width-1;g++)field[g,f]=
    field[g,f-1];}if(FindMistake()){for(int c=0;c<4;c++)field[shape[1,c],--shape[0,c
    ]]++;SetShape();}FillField();}void i(object a,KeyEventArgs b){switch(b.KeyCode){
    case Keys.A:for(int c=0;c<4;c++)shape[1,c]--;if(FindMistake())for(int c=0;c<4;c
    ++)shape[1,c]++;break;case Keys.D:for(int c=0;c<4;c++)shape[1,c]++;if(
    FindMistake())for(int c=0;c<4;c++)shape[1,c]--;break;case Keys.W:var d=new int[2
    ,4];Array.Copy(shape,d,shape.Length);int e=0,f=0;for(int c=0;c<4;c++){if(shape[0
    ,c]>f)f=shape[0,c];if(shape[1,c]>e)e=shape[1,c];}for(int c=0;c<4;c++){int g=
    shape[0,c];shape[0,c]=f-(e-shape[1,c])-1;shape[1,c]=e-(3-(f-g))+1;}if(
    FindMistake())Array.Copy(d,shape,shape.Length);break;}}public void SetShape(){
    var a=new Random(DateTime.Now.Millisecond);switch(a.Next(7)){case 0:shape=new
    int[,]{{2,3,4,5},{8,8,8,8}};break;case 1:shape=new int[,]{{2,3,2,3},{8,8,9,9}};
    break;case 2:shape=new int[,]{{2,3,4,4},{8,8,8,9}};break;case 3:shape=new int[,]
    {{2,3,4,4},{8,8,8,7}};break;case 4:shape=new int[,]{{3,3,4,4},{7,8,8,9}};break;
    case 5:shape=new int[,]{{3,3,4,4},{9,8,8,7}};break;case 6:shape=new int[,]{{3,4,
    4,4},{8,7,8,9}};break;}}public bool FindMistake(){for(int a=0;a<4;a++)if(shape[1
    ,a]>=width||shape[0,a]>=height||shape[1,a]<=0||shape[0,a]<=0||field[shape[1,a],
    shape[0,a]]==1)return true;return false;}}


    1. Vest
      22.12.2018 18:53

      Попробуйте ещё minify: codebeautify.org/csharpviewer выйдет ещё немного меньше :)


  1. rstepanov
    21.12.2018 18:48
    -1

    Тоже тетрис
    Сколько строк — не считал.


  1. FadeToBlack
    22.12.2018 07:52

    12


  1. MTonly
    22.12.2018 18:11
    +1

    Не хватает ссылки на GitHub-репозиторий. ;-)


  1. perfect_genius
    22.12.2018 18:24
    +1

    Но почему дно стакана шириной не в стандартные 10 пикселей?


    1. Limpich Автор
      22.12.2018 18:39

      Признаю, мой косяк) Но это легко изменяется в константе width.


  1. CrashLogger
    22.12.2018 19:26
    +1

    Тоже тогда похвастаюсь?

    Tetris на JS
    github.com/pingwiner/tetris

    Tetris на С для фитнес-трекера Gear Fit 2 Pro
    github.com/pingwiner/gear_fit2_tetris


  1. iga2iga
    23.12.2018 05:17

    Ну такое…
    Вот тут полноценный тетрис со спец.эффектами целиком на GLSL. (кроме опроса клавиатуры разумеется и инициализации)
    www.shadertoy.com/view/4d33Dj

    ps. Чтобы поиграть надо закомментировать первый return 0; в Buffer A


  1. Mikluho
    23.12.2018 15:55

    А я вот не понял, при чём тут C#?, и уж тем более «интересный код»?
    Ни ООП, ни мощи .Net…
    Даже единственное Linq-выражение записано в SQL-стиле, хотя, что мешало написать:
    var cross = Enumerable.Range(0, field.GetLength(0)).Count(j => field[j, i] == 1);


  1. perlestius
    23.12.2018 15:59

    А мне еще очень давно в книге (по-моему, это было «Занимательное программирование»
    М. Мозгового) попадались исходники тетриса, где поворот фигур был реализован с помощью матрицы поворота.