Посоветуйте алгоритм для неструктурируемых переходов

Тема в разделе "Флудилка", создана пользователем Tomasina, 1 сен 2016.

  1. Tomasina

    Tomasina Сушитель лампочек Модератор

    Делаю небольшую викторину. Есть две кнопки, по нажатию которых воспроизводятся звуковые файлы в заданной последовательности.
    Сама таблица последовательностей такая:
    2016-09-01_21-49-11.png
    Т.е. по нажатии кнопки А воспроизводится файл №1,
    если потом снова нажата кнопка А, то воспроизводится файл №2, но если нажата кнопка Б, то прыгаем сразу на файл №4.
    Пока задумка такая: массив из 3хN ячеек, где записаны нужные индексы. Отслеживаем текущее значение и по нажатии кнопки переходим по индексу А или Б.
    Но это получается очень громоздко (много if или switch) и сложно в отладке.
    Может есть более оптимальный способ?

    P.S. Если очень надо в целях автоматизации, можно в каждом блоке вопросов сделать одинаковое количество ячеек (просто некоторые будут пустые, без вопросов).
     
  2. DIYMan

    DIYMan Guest

    Если я правильно понял, то:

    1. Вход (первое нажатие на кнопку А) - играем мелодию N;
    2. Выход №1 (второе нажатие на кнопку А) - играем мелодию M;
    3. Выход номер 2 (нажатие на кнопку Б) - играем мелодию Z.

    Судя по табличке - бинарное дерево в чистом виде. Можно построить в памяти сразу, и тогда не надо будет никакого множества switch и if - будут просто работать переходы. Если память не жмёт - можно структурками изобразить, что-то типа этого:
    Код (C++):
    struct leaf;

    typedef struct
    {
    leaf* left; // ссылка на левый узел
    leaf* right; // ссылка на правый узел
    byte melody; // номер мелодии
    } leaf;
    И простроить дерево при старте, хоть из каких исходных данных. Если такой подход не устроит - будем думать :)
     
  3. Tomasina

    Tomasina Сушитель лампочек Модератор

    Эм, а дерево как хранить, в массиве? Разбираюсь, вроде все понятно.
    Допустим, текущий файл #5, как передать в структуру что нажата кнопка Б?
     
    Последнее редактирование: 2 сен 2016
  4. ostrov

    ostrov Гуру

    Где это отображено в таблице?
     
  5. DIYMan

    DIYMan Guest

    Смотрите: вы отстраиваете дерево при старте. Если нажата кнопка А - то путь в дереве - по ветке left. Если нажата кнопка B - то путь в дереве - по ветке right. Допустим, у нас уже есть сформированное дерево, и мы стоим у ствола:

    Код (C++):

    leaf* currentLeaf= getRoot();

    playMelody(currentLeaf->melody);

    if(isAButtonPressed())
    {
      currentLeaf = currentLeaf->left;
    }
    else
      currentLeaf = currentLeaf->right;

    playMelody(currentLeaf->melody);

     
    Дерево можно отстраивать при старте, как я уже писал. Это не совсем тривиально порой, но - вполне решаемо. На выходе получите любую нужную вам конфигурацию, в том числе такие случаи, как закольцованность и прочие ништяки. Про инициализацию дерева - вопрос отдельный, с наскока не решается. Я же говорю - если этот вариант устроит, будем продолжать. Если нет - всегда можно найти вариант попроще. То, о чём ведётся речь, не что иное, как связанный список. Вернее, одна из его разновидностей.
     
    Последнее редактирование модератором: 2 сен 2016
  6. Tomasina

    Tomasina Сушитель лампочек Модератор

    Ага, теперь понятно.
     
  7. ostrov

    ostrov Гуру

    Нет, правда, чем плоха идея с массивом 3х20? Задача то простая совсем. зачем решать ее сложно? Чтобы легче запутаться и труднее потом исправлять если что?

    Поясните для начинающего смысл пложения сущностей без нужды.
     
  8. DIYMan

    DIYMan Guest

    Про инициализацию дерева при старте: во-первых, надо знать кол-во шагов всего, и инициализировать все структуры:
    Код (C++):

    #define COUNT_STEPS 20

    leaf* tree[COUNT_STEPS];

    void setup()
    {
      for(byte i=0;i<COUNT_STEPS;i++)
      {
          tree[i] = new leaf;
      }
    }
     
    Далее - надо заполнить связи структур. Допустим, у вас есть массив с настройками вида
    Код (C++):

    typedef struct
    {
      byte enter_melody; // мелодия, играющаяся на входе
      byte a_pressed; // мелодия, играющаяся по кнопке A
      byte b_pressed; // мелодия, играющаяся по кнопке Б
    } byte_code;

    byte_code codes[COUNT_STEPS] = {
    {1,2,4},
    {3,1,5},
    // и т.п.
    };
     
    Тогда простройка дерева будет выглядеть так:
    Код (C++):

    for(byte i=0;i<COUNT_STEPS;i++)
    {
       byte_code bc = codes[i];
       leaf* l = tree[i];
       l->left = tree[bc.a_pressed];
       l->right = tree[bc.b_pressed];
      l->melody = bc.enter_melody;
    }
     
    Писал навскидку, надеюсь, подход понятен. Дальнейшая обработка - мизерна и без чудовищных switch и if ;)
     
  9. DIYMan

    DIYMan Guest

    Универсальность. Структурированность. Документированность. Лёгкость расширения и сопровождения. Системность подхода.

    Достаточно? Не навязываю, просто я так привык - если можно задачу обобщить без больших телодвижений - то это и делаю. Я уже почти весь скетч тут в ответах написал, собственно - где там сложность? А вот если делать в лоб - сложность обязательно будет, если надо будет перестроить граф, и придётся копаться в кишочках, продираясь через череду if и прочих условий. Зачем, если можно сначала полчаса потерять, а потом за пять минут долететь? (с) "Крылья, ноги и хвосты".
     
  10. ostrov

    ostrov Гуру

    На мой взгляд массив мелодий 3х20, в виде копии таблицы из первого сообщения, проще, структурированней, легче для понимания, исправлений и дополнений, чем "бинарное дерево". Плюс функция обработки нажатий на пару тройку строк, а не чудовищных if и swith. Могу ошибаться, конечно, но лучше полчаса не терять, а сразу за пять минут долететь.
     
  11. ostrov

    ostrov Гуру

    1. Ждем нажатия кнопки А, играем melody(0)(i)
    2. Ждем нажатия кнопки А или Б, играем melody(1)(i) или melody(2)(i)
    3. Ждем отпускания нажатой кнопки.
    4. i++, повторяем п.1 - 3 20 раз.

    Проще некуда, и как ни крути, массив мелодий все равно создавать, ибо они не вычисляемы, а директивны.

    Или я не так понял задание?

    Просто я недавно делал школьный звонок по похожему принципу: массив из режимов и звонков, работает отлично и программа очень наглядна, исправить/добавить/удалить что то расписания может даже не автор.
     
    Последнее редактирование: 2 сен 2016
  12. AlexU

    AlexU Гуру

    С использованием массивов задача проще решается. Вот только хватит одного массива и индекса текущей мелодии:
    Код (C++):


    // индекс текущей мелодии
    // 0 -- начальное состояние: ни одна кнопка ни разу ни нажата
    volatile unsigned char currentMelody = 0;

    // массив индексов мелодий (заполняется в соответствии с таблицей)
    // первая размерность -- количество вопросов
    // вторая -- количество кнопок (кнопок может быть любое количество)
    unsigned char melodies[18][2] = {
         {1, 1}, // при первом нажатии любой кнопки играем первую мелодию
         {2, 4},
         {3, 4},
         {3, 4},
          .....
    };
     
    Далее:
    Код (C++):

    // buttonIndex = 0, когда нажата кнопка А
    // buttonIndex = 1, когда нажата кнопка Б
    // и т.д.
    // возвращает номер мелодии, которую нужно исполнить
    unsigned char onButtonPressed(unsigned char buttonIndex)
    {
      currentMelody = melodies[currentMelody][buttonIndex];
      return currentMelody;
    }
     
     
  13. DIYMan

    DIYMan Guest

    Ребят, я не спорю, что можно проще и в лоб. Я так привык, поймите - когда оказывается, что задача включает не только это, но и ещё чего-то. И тогда - всё: ни убавить, ни прибавить, и начинаются пляски разного рода. Поэтому, когда я не полностью знаю, в чём заключается задача - предпочитаю выдавать более общее, расширяемое в дальнейшем решение.

    Без претензий.
     
    ostrov нравится это.
  14. ostrov

    ostrov Гуру

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

    Но задача не настолько проста, насколько сложно ваше решение, а потому ИМХО оно не наглядно и сложно в понимании для тех у кого нет ученой степени. Если с автором что случится, его менее талантливые замы исправить ничего не смогут и им придется снова создавать на форуме подобную ветку. ))
     
    DIYMan нравится это.