Как я могу определить, какие плитки перемещаются и сливаются в моей реализации 2048?

Я создаю игру 2048 WinForms для удовольствия.

Обратите внимание, что речь идет не о 2048 AI. Я просто пытаюсь сделать игру 2048, которую могут сыграть люди.

Сначала я решил использовать 0-17 для представления плиток. 0 представляет собой пустую плитку. 1 представляет собой 2 плитки. 2 представляет собой 4 плитки. 3 представляет собой 8 плиток и т.д.

Затем я подумал о том, как вычислить полученную доску, учитывая направление движения и доски перед движением. Вот о чем я думал:

  • Чтобы двигаться вверх, он просто поворачивает доску против часовой стрелки на 90 градусов, перемещается влево, затем поворачивает плату обратно
  • Чтобы двигаться вправо, он просто поворачивает плату по часовой стрелке на 180 градусов, перемещается влево, затем поворачивается назад
  • Чтобы двигаться вниз, он просто поворачивает плату по часовой стрелке на 90 градусов, перемещается влево, а затем поворачивается назад.

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

  • Преобразуйте каждый из начальных целых чисел в символы, добавив 96 и отбрасывая на char. Теперь обратный тик (') представляет собой пустую черепицу, a представляет собой 2 плитки, b представляет собой 4 плитки и т.д., А также путь к p.
  • Объедините символы, чтобы сформировать 4 строки, каждая из которых представляет ряд досок.
  • Примерная панель может выглядеть так:

    aa''
    ''''
    '''b
    ''cb
    
  • Для каждой строки,

    • Удалите все обратные тики
    • Используйте регулярное выражение (да, я использую регулярное выражение в игре 2048) ([ap])\1 и получаю первое совпадение строки.
    • замените первый матч новой плиткой
    • сопоставьте остальную часть строки, которая еще не была сопоставлена, пока не будет найдено больше совпадений.
  • Вставьте строку вправо, если она содержит менее 4 символов.
  • Поверните каждую строку обратно в массив целых чисел, вычитая 96

Так вот как я оцениваю каждую строку:

    int[] EvaluateRow(int[] row) {
        // RowToString converts an int[] to a string like I said above
        StringBuilder rowString = new StringBuilder(RowToString(row));
        rowString.Replace("'", "");
        var regex = new Regex("([a-p])\\1");
        int lastIndex = -1;
        while (true) {
            var match = regex.Match(rowString.ToString(), lastIndex + 1);
            if (match.Success) {
                // newChar is the new tile after the merge
                char newChar = (char)(match.Value[0] + 1);
                rowString.Remove(match.Index, match.Length);
                rowString.Insert(match.Index, newChar);
                lastIndex = match.Index;

                Score += // some calculation for score, irrelevant
            } else {
                break;
            }
        }
        // StringToRow converts a string to an int[]
        return StringToRow(rowString.ToString());
    }

Тем не менее, есть действительно большая проблема с моим текущим алгоритмом. Этот алгоритм говорит мне только о конечном результате движения, но я не знаю, какой ящик для картинок (я использую окна с картинками, чтобы показывать плитки). Мне нужно перемещать, сколько пробелов должно быть перемещено в каждом поле изображения, и какой снимок ящики должны показать новое изображение. Я действительно не хочу использовать другое решение, и хочу внести некоторые изменения в свое текущее решение.

Вот что мне нужно получить от каждой строки (строки):

  • List<(int x, int spaces)>. Каждый элемент представляет, какую плиту нужно перемещать (координату x) и сколько пробелов он должен перемещать (spaces).
  • List<int>. Каждый элемент представляет x координаты плиток, которые объединены.

Как я могу получить эту информацию из строки строки? Пример:

Строка строки:

'a'a

будет создавать список, подобный [(1, 1), (3, 3)] и другой список, подобный [1].

Ответ 1

Я не думаю, что превращение в персонажей действительно добавляет что-нибудь полезное. Если вы придерживаетесь представления числа (0 = пустое), вы можете использовать следующую логику, чтобы найти как целевую конфигурацию, так и какой блок отправился туда. Это псевдокод (row задана):

fromTo = [-1, -1, -1, -1];
result = [0, 0, 0, 0];
prevVal = 0;
pos = 0;

for (i = 0; i < 4; i++) {
    if (row[i]) { // Not empty
        if (row[i] == prevVal) {
            result[pos-1]++; // merged
            fromTo[i] = pos-1;
            prevVal = 0; // avoid triple merge
        } else {
            result[pos] = row[i];
            fromTo[i] = pos;
            prevVal = row[i];
            pos++;
        }
    }
}

Теперь массив fromTo будет указывать для каждого индекса, куда пошел блок в этом исходном положении. result будет иметь окончательные значения. Из этих двух частей информации вы также можете узнать, какие блоки были объединены. Блок в исходном положении я объединяется в result[fromTo[i]] != row[i]. Вы также знаете расстояние, на которое будет двигаться блок: i - fromTo[i]. Короче говоря, у вас есть вся информация, чтобы настроить анимацию для каждого блока.

Примеры

row         |   fromTo       |   result
------------+----------------+-----------
[0,1,0,1]   |  [-1,0,-1,0]   |  [2,0,0,0]
[0,1,1,1]   |  [-1,0,0,1]    |  [2,1,0,0]
[1,1,1,1]   |  [0,0,1,1]     |  [2,2,0,0]
[1,2,2,3]   |  [0,1,1,2]     |  [1,3,3,0]