Нужна помощь в оптимизации кода игры «Змейка» для Arduino Nano
Вот код:
https://pastebin.com/ypWe4NZw
Вот что получается при компиляции:
Sketch использует 13086 байт (42%) пространства для хранения программ. Максимум — 30720 байт. Глобальные переменные используют 1456 байт (71%) динамической памяти, оставляя 592 байта для локальных переменных. Максимум — 2048 байт.
Все идет гладко до getNextFood(), строка 194. Я пытаюсь создать LinkedList целых чисел от 0 до 255 (так как я использую сетку светодиодов 16x16). Однако мой список заполняется только до 39, затем остальные устанавливаются в ноль. Я подозревал, что у меня не хватает памяти, поэтому я попытался оптимизировать несколько вещей и смог получить 42. Я изменил все целые числа на байты в функции getNextFood и получил 61, но это каким-то образом сломало мою программу (цикл inf).
Есть ли какие-либо предложения, как это исправить? Если я смогу уместить этот список, программа в основном готова, мне больше нечего добавить, если она работает так, как задумано.
И, кстати, почему у меня вообще заканчивается оперативная память? Может кто-нибудь объяснить, какие части программы пожирают эти байты во время выполнения (потому что я ожидал, что список целых чисел займет около 512 байт, оставляя 80 байт, так почему же я обрезаюсь около 40 целых чисел или 80 байт?)
Спасибо.
Редактирование 1: Люди указали, что связанные списки занимают много места. Я использую эту библиотеку, потому что программа спроектирована так, что мне нужно было создать массив, который мог бы менять размер. Мне нужно было иметь возможность добавлять и удалять элементы. Может быть, есть лучший способ сделать это, я не знаю. Я думал о том, чтобы сделать один массив 16x16 для всего этого и использовать разные числа для каждого типа элементов. Я не знаю, как бы я делал движение змеи в этом случае, потому что каждый элемент «связан» с тем, который находится перед ним. Если кто-то знает похожую библиотеку, которая сэкономит место, я был бы рад попробовать. Также, возможно, есть способ избежать использования списка tempSnake.
Edit2: По предложению Пола я переписал код, используя один массив 16x16 для представления всего игрового поля, устранив необходимость в LinkedList. Однако, похоже, я все еще не дотягиваю, если только в моем коде нет других ошибок. Программа сбрасывается, когда я пытаюсь сгенерировать плитки еды. https://pastebin.com/ZU4hX477
@user6615434, 👍-1
Обсуждение1 ответ
Лучший ответ:
16x16 = 256
Итак, существует 256 возможных мест, куда можно поместить кусочек змеи. Максимальная длина змейки составит 256 (полностью заполнит доску).
Таким образом, если мы возьмем массив байтов (0-255) длиной 256 байт, мы сможем полностью отобразить змею.
С каждым шагом (если только змея не съела немного еды) вы продвигаете ее голову вперед на один шаг и удаляете последнюю часть.
Так что если мы сделаем новую голову значением "snakeLength" и сделаем каждую часть тела -1 каждый раунд. Мы получим этот эффект "затухания".
Например:
[ 6][5][4] (all -1, head down) [ 5][4][3]
[ 0][0][3] [ 6][0][2]
[ 0][1][2] [ 0][0][1]
Это, возможно, не самый эффективный метод (особенно если вы его масштабируете), но в этом случае логика будет очень простой.
Если голова столкнется с позицией, которая не равна 0, игра будет окончена. Для еды/яблока вы можете использовать значение x/y.
Если пробел не равен 0, сделайте его равным -1.
Преимущество этого в том, что вам не нужно выделять память во время выполнения и вы точно знаете, сколько ее используете. Эта настройка также должна помочь вам избежать постоянного «копирования» всего списка. По сути, вы можете пройтись по массиву и сделать -1 для всех, кроме тех, которые равны 0.
Мне нравится эта идея. Что вы подразумеваете под значением x/y для еды? Например, просто хранить где-то строку и столбец еды? Я думал использовать какое-нибудь отрицательное число, например, -1., @user6615434
Отличная идея. Можно зарезервировать значение 255 для фруктов и добавить проверку, чтобы оно не было равно 255 перед уменьшением. Можно зарезервировать 254 для стен. 253 для телепорта q-;, @Gerben
Всё ещё не хватает оперативной памяти, LOL! 225 байт доступно во время компиляции, вылетает при генерации новых плиток еды... https://pastebin.com/ZU4hX477, @user6615434
Хм, вам следует использовать байты (значения 8 бит/1 байт), если вы используете -1, вы либо будете использовать целое число (2 байта), либо у вас будет диапазон от -128 до +128., @Paul
Хм, проверьте переменную 'int foodSquares[numZeros] = { 0 };', которая использует 255x 2 байта. К тому же, ваша игровая доска использует int вместо byte (который вдвое меньше)., @Paul
Кроме того, светодиоды занимают довольно много места, если вы используете светодиодную ленту на базе SPI вместо неопикселей, вы можете во время выполнения рассчитать значения (так как вы не привязаны к тактовой частоте неопикселей), поэтому их не нужно сохранять., @Paul
- Nano отправить 4 значения банка в Uno пожалуйста помогите с кодом
- Как объявить массив переменного размера (глобально)
- avrdude ser_open() can't set com-state
- Загрузка Arduino Nano дает ошибку: avrdude: stk500_recv(): programmer is not responding
- Какое максимальное энергопотребление Arduino Nano 3.0?
- Как навсегда изменить скорость передачи данных ESP8266 (12e)?
- Arduino nano как клавиатура HID
- Питание светодиодной ленты - Сколько ампер?
Сайты Stack Exchange предназначены для задач, которые можно кратко сформулировать на странице вопроса. Вам нужно будет сузить область действия до того, что можно представить без ссылки. **Зачем вы вообще используете связанный список?** Это будет чрезвычайно затратно для такой системы. Вам следует использовать простой массив, но если у вас нет управления яркостью, даже это не будет излишним, вероятно, вам нужен растровый массив с 1 битом на пиксель. Вы также можете рассмотреть более подходящую платформу, например, микроконтроллер ARM с большим объёмом памяти; некоторые работают с Arduino IDE., @Chris Stratton
Если у вас мало оперативной памяти, старайтесь избегать создания полных копий линейно-развёрнутых списков. Кроме того, вы написали
//implement modulus for position: этот оператор уже реализован в C++, и вы можете написатьnewRow = (newRow+1)%16и т. д. Но учтите, что для достижения желаемого результата числа должны быть беззнаковыми., @Edgar BonetВам *действительно* нужна сложность и раздутость связанных списков?, @Majenko
Использование макроса F для Serial.print позволит сэкономить несколько байт. Serial.println(F(“example”));, @Gerben
Вам не нужен связный список для определения соседей ячеек сетки; это довольно простая математическая операция с некоторыми ограничениями по краям. Грубо говоря, левое направление соответствует индексу -1, правое направление — индексу +1, верхнее направление — индексу -16, нижнее направление — индексу +16 и т.д., после чего проверяется ограничение по краям. Наличие связного списка для самой *змеи* в некоторой степени разумно, хотя вместо него, возможно, стоило бы использовать массив фиксированной длины с разностями индексов для следующего сегмента., @Chris Stratton