Реализация игры 2048 на Windows Forms. Без анимаций и таймеров — всё построено на прямом управлении Label-элементами, тройных вложенных циклах и линейном поиске. Поддерживается выбор размера поля от 4×4 до 8×8.
- 🎯 Полная механика игры 2048: движение, слияние плиток, подсчёт очков
- 🧩 Игровое поле собрано из WinForms-элементов
Label - 📐 Настраиваемый размер поля — можно играть на сетке от 4×4 до 8×8
- 💾 Сохранение результатов всех игроков в
result.jsonс поиском лучшего счёта по имени - 🏁 Игра продолжается и по достижении 2048, поражение засчитывается если нет доступных ходов
- 📋 Используется Newtonsoft.Json для сериализации и собственный
FileManagerдля работы с файлами
Игровое поле — это не просто двумерный массив чисел, а сетка из Label-элементов. Её размер задаётся переменной mapSize и может быть от 4 до 8. Все алгоритмы (движение, слияние, спавн) написаны через циклы, привязанные к mapSize, поэтому работают корректно при любом размере поля.
При нажатии на стрелки происходит следующее:
- Движение плитки с помощью тройного цикла.
- Сравнение текста — у двух Label-элементов считывается свойство
Text. Если значения равны — происходит слияние с удвоением. - Визуальное обновление — победившая плитка перекрашивается, проигравшая очищается и становится серой.
- Генерация новой плитки — после каждого хода в случайную свободную клетку добавляется 2 (с шансом 75%) или 4 (с шансом 25%).
private void PressRight()
{
for (var i = 0; i < mapSize; i++)
{
for (var j = mapSize - 1; j >= 0; j--)
{
if (labelsMap[i, j].Text != string.Empty)
{
for (var k = j - 1; k >= 0; k--)
{
if (labelsMap[i, k].Text != string.Empty)
{
if (labelsMap[i, j].Text == labelsMap[i, k].Text)
{
var number = int.Parse(labelsMap[i, j].Text);
score += number * 2;
labelsMap[i, j].Text = (number * 2).ToString();
SetColor(labelsMap[i, j]);
labelsMap[i, k].Text = string.Empty;
labelsMap[i, k].BackColor = Color.Silver;
}
break;
}
}
}
}
}
GenerateNumber();
ShowScore();
}Поиск пустой клетки для спавна
private List<(int, int)> LinearSearch()
{
var emptyLabels = new List<(int Row, int Column)>();
for (var i = 0; i < mapSize; i++)
{
for (var j = 0; j < mapSize; j++)
{
if (labelsMap[i, j].Text == string.Empty)
{
emptyLabels.Add((i, j));
}
}
}
return emptyLabels;
}💾
Сохранение результатов
Все результаты хранятся в файле result.json. После каждого завершения игры запись добавляется в общую таблицу рекордов.
// Получение лучшего результата игрока по имени
public static int GetBestResult(string name)
{
var usersResult = GetAll();
var results = new List<string[]>();
// Фильтруем — собираем все партии этого игрока
foreach (var result in usersResult)
{
if (result[0] == name)
{
results.Add(result);
}
}
// Ищем максимальный счёт линейным перебором
var bestResult = 0;
foreach (var result in results)
{
if (int.Parse(result[1]) > bestResult)
{
bestResult = int.Parse(result[1]);
}
}
return bestResult;
}// Сохранение всего списка результатов в JSON
public static void Save(List<string[]> results)
{
var data = JsonConvert.SerializeObject(results, Formatting.Indented);
FileManager.Save(Path, data);
}🎮 Как играть
- При запуске выберите размер поля (от 4×4 до 8×8).
- Управляйте плитками стрелками на клавиатуре (← ↑ → ↓ или клавишами wasd).
- Одинаковые плитки при столкновении сливаются, удваивая номинал.
- Цель — добраться до плитки с числом 2048 или выше.
- Игра заканчивается в случае выхода или поражением (не осталось ходов).
🖼️ Как это выглядит
- Прямое управление Label-элементами.
- Динамическая генерация интерфейса.
- Тройные вложенные циклы.
- Линейный поиск.
- Newtonsoft.Json —
JsonConvert.SerializeObject()сFormatting.Indented,JsonConvert.DeserializeObject<List<string[]»(). - Собственный FileManager.
- ООП.
- Работа со строками и числами —
int.Parse()/ToString()для преобразования значений на плитках.