Space Invaders в 2 строчки кода (c#)

от автора

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

Игра представляет собой вольный ремейк культовой игры, выпущенной в 1978 году.

Несмотря на небольшой размер, игра поддерживает анимацию и звуковое сопровождение.

Как говорится:
Везет программистам, захотел поиграть — написал игру.

Друзья, в статье есть тег — Юмор, не стоит использовать такое в продакшене.

Update1: Добавил описание методик, используемых для сжатия кода.

Описание

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

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

Основые особенности

Мрачная атмосфера

Динамичный геймплей

Высока реиграбельность

Полное погружение

Поддержка звука

Поддержка анимаций

Разрушаемое окружение

Отстреливание конечностей

Управление

A — движение влево

D — движение вправо

Space — выстрел

Требование к коду

Каждая фигурная скобка на отдельной строке

У встроенных операторов for, if и т.д. тело находится на отдельной строке

Оператор — точка с запятой, который завершает выражение — один на строке

Ссылки

Ссылка на весь проект

Ссылка на исходный код игры

Ссылка на бинарное демо

@ozzy-ext

Отдельное спасибо

Как это реализовано

Изначальный код, очевидно, написан обычным способом, но с определенными ограничениями, так как не весь код можно так сжать.

Ссылка на оригинальный код есть в том же проекте

Описание будет псевдокодом.

Карта нарисована в текстовом файле и просто вставлена как данные.

Позже я ее немного сжал, но это было не обязательно.

Вместо подряд идущих символов, можно написать например:

new string(‘=’, 78)

Вместо повторяющихся внутри символов, можно сделать замену, а потом развернуть.

Например, вместо 8 пробелов, сделать одну точку, а потом развернуть:

“карта”.Replace(“.”, “        ”)

Часть переменных, а позже и все остальные, содержатся в отдельном массиве d. В виде char к которым мы обращаемся по индексу. Cделано это потому, что в определении цикла for, в первом блоке, все данные должны быть одного типа. Поэтому там переменные в виде строки и карта.

Основной цикл игры — бесконечный. Внутри несколько циклов прохода по карте. Часть из проходов в прямом, часть в обратном порядке, чтобы можно было двигать объекты в разные стороны.

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

Только так можно соединить вместе множество изменений, в одну длинную строку, используя вложенные операторы ?:

m[i] = (условие1) ? ‘новый символ’ : m[i];

m[i] = (условие2) ? ‘новый символ’ : m[i];

Из двух выражений можно сделать одно:

m[i] = (условие1) ? ‘новый символ’ : ((условие2) ? ‘новый символ’ : m[i]);

На данном этапе, мы имеем один глобальный цикл и множество циклов (проходов), по карте, по одним и тем же индексам.

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

Изначальные циклы:

for (i; i<count; i++)

    m[i] = (условие1) ? ‘новый символ’ : m[i];

for (i; i<count; i++)

    m[i] = (условие2) ? ‘новый символ’ : m[i];

Общий цикл:

for (i2;i2<count * 2;i2++)

{

    var i = i2 % count;

    var p = i / count;

    if (p == 0)

        m[i] = (условие1) ? ‘новый символ’ : m[i];

    if (p == 1)

        m[i] = (условие2) ? ‘новый символ’ : m[i];

}

Далее проверка на проход, тоже объединяется с общим выражением:

m[i] = (p == 0) ? ((условие1) ? ‘новый символ’ : m[i]) : m[i];

После этого у нас остается главный бесконечный цикл.

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

Далее, оказалось, что в последнюю часть цикла for, где мы обычно делаем i++, можно через запятую, вписывать любые выражения, которые не возвращают результата.

И уже наша конструкция приобретает вид:

Первая строка, бесконечный for. Внутри него в левой части два наших массива с картой и данными, в центре true, так как цикл бесконечный, а в правой части через запятую, наши операторы: sleep, beep, write line

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

Третья строка, пустой оператор ;

Такой изначально и был результат. Чтобы сжать еще сильнее, нужно объединить два цикла for. Но есть одна проблема. Нам нужен первый проход, который рисует карту и издает звук. Но эти выражения ничего не возвращают, не получится их описать конструкцией ?:

Нужно придумать выражение, которое ничего не возвращает, захватывает переменную из цикла, в зависимости от нее, выполняет или не выполняет другое выражение, которое тоже ничего не возвращает.

В этом мне помог мой друг @ozzy-ext, написав выражение:

new int[] { i }.Where(v => v % 2 == 0).ToList().ForEach(v =>  _output.WriteLine($"Beep - {v}"));

После этого, добавился еще один первый проход в цикл for, в котором и происходит отрисовка карты. И добавился последний проход, который сбрасывает все переменные, потому что цикл стал бесконечным.

Таким образом и получился один цикл for в котором вся игра, а в цикле один пустой оператор.

Всем спасибо за внимание!


ссылка на оригинал статьи https://habr.com/ru/articles/842754/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *