Специально для русскоязычного населения буду описывать зарубежные примеры на русском языке. Логическую игру-головоломку "пятнашки" знают все (или почти все). Поле представляет собой матрицу (двумерный массив ) размерностью 4x4, один из элементов пустой. Перемешанные цифры, от 1 до 15 помещаются в это поле, задача, выстроить эти цифры в порядке возрастания. Разберём одну из реализаций этой задачи, код написан на C++. Code #include <iostream> #include <ctime>
enum EMove {keUp = 'w', keDown = 's', keLeft = 'a', keRight = 'd'};
// Function declarations void InitializeBoard(char[4][4]); void PrintBoard(char[4][4]); void LocateSpace(int&, int&, char [4][4]); void Randomize(char[4][4]); void Move(char[4][4], const EMove);
int main() { char caaBoard[4][4]; InitializeBoard(caaBoard); Randomize(caaBoard);
using namespace std; do { PrintBoard(caaBoard); cout << endl << "w = Up, s = Down, a = Left, d = Right" << endl; char cNextMove; cin >> cNextMove; EMove eNextMove = (EMove)cNextMove; Move(caaBoard, eNextMove); cout << endl; } while (true);
return EXIT_SUCCESS; }
void InitializeBoard(char caaBoard[4][4]) { const char kcaaInitial[4][4] = { {'1', '2', '3', '4'}, {'5', '6', '7', '8'}, {'9', 'A', 'B', 'C'}, {'D', 'E', 'F', ' '}};
for (int iRow = 0; iRow < 4; ++iRow) for (int iCol = 0; iCol < 4; ++iCol) caaBoard[iRow][iCol] = kcaaInitial[iRow][iCol]; }
void PrintBoard(char caaBoard[4][4]) { using namespace std; for (int iRow = 0; iRow < 4; ++iRow) { for (int iCol = 0; iCol < 4; ++iCol) cout << caaBoard[iRow][iCol]; cout << endl; } }
void LocateSpace(int& irRow, int& irCol, char caaBoard[4][4]) { for (int iRow = 0; iRow < 4; ++iRow) for (int iCol = 0; iCol < 4; ++iCol) if (caaBoard[iRow][iCol] == ' ') { irRow = iRow; irCol = iCol; } }
void Randomize(char caaBoard[4][4]) { using namespace std; srand((unsigned int)time(0)); for (int iIndex = 0; iIndex < 1000000; ++iIndex) { const int kiNextMove = (rand() % 4); switch (kiNextMove) { case 0: { Move(caaBoard, keUp); break; } case 1: { Move(caaBoard, keDown); break; } case 2: { Move(caaBoard, keLeft); break; } case 3: { Move(caaBoard, keRight); break; } } } }
void Move(char caaBoard[4][4], const EMove keMove) { int iRowSpace; int iColSpace; LocateSpace(iRowSpace, iColSpace, caaBoard); int iRowMove(iRowSpace); int iColMove(iColSpace); switch (keMove) { case keUp: { iRowMove = iRowSpace + 1; break; } case keDown: { iRowMove = iRowSpace - 1; break; } case keLeft: { iColMove = iColSpace + 1; break; } case keRight: { iColMove = iColSpace - 1; break; } } // Make sure that the square to be moved is in bounds
if (iRowMove >= 0 && iRowMove < 4 && iColMove >= 0 && iColMove < 4) { caaBoard[iRowSpace][iColSpace] = caaBoard[iRowMove][iColMove]; caaBoard[iRowMove][iColMove] = ' '; } } Рассмотрим основные функции. Code void InitializeBoard(char[4][4]) Инициализация доски, если дословно. Суть данной функции заключается в заполнении массива, переданного в параметрах значениями локального массива этой функции. Массив содержит 16 символов (видимо для краткости вывода на экран автор представил числа от 10 до 15 в шестнадцатеричном виде) и двойной цикл (так как массив двумерный) переносящий значения массива в тот, который мы укажем в параметрах. Code void PrintBoard(char caaBoard[4][4]) Дословно, печать доски. Выводит содержимое доски на экран. Также используется двойной цикл, из-за двумерности массива. Далее про двойные циклы я буду умалчивать, так как данный факт должен быть вами уже осознан. Code void LocateSpace(int& irRow, int& irCol, char caaBoard[4][4]) Функция возвращает координаты пустой клетки. Реализована путём поиска пробела (символа) в нашем массиве и возвращение индексов в случае нахождения. Обратим внимание на то, что первые два параметра указывают на адрес переменных, введённых в параметрах функции. Далее об этом я расскажу, здесь сделал небольшой акцентик просто. Code void Randomize(char caaBoard[4][4]) Функция перемешивающая значения нашей доски. Для качественного перемешивания используется функция srand() с параметром (unsigned int)time(0) возвращающим системное время и приведённого к виду без знакового целого числа. Если данную функцию не использовать то функция rand() будет выдавать одно и то же число. Также для передвижения клеток на доске используется функция Move, которую мы рассмотрим далее. Code void Move(char caaBoard[4][4], const EMove keMove) Рассмотрим логику. Сначала мы определяем положение путой клетки, для этого применяем функцию LocateSpace. Вот здесь и обратим внимание, что передаётся в эту функцию. Перед ней объявлены две переменные целого типа, int - это iRowSpace и iColSpace. В параметрах не указывается никаких дополнительных знаков. Функция работает с адресами этих переменных, т.е. грубо говоря переменные остаются здесь, а мы переходим к функции, которая находится в другом месте памяти и она меняет значения этих переменных не на прямую, а через их адрес. (Кстати можете поискать пример работы с указателями в статьях, может будет понятнее) Далее мы видим интересный фокус с инициализацией вновь объявленных переменных: Code int iColMove(iColSpace); int iRowMove(iRowSpace); в принципе то же, что и Code int iColMove = iColSpace; int iRowMove=iRowSpace; НО, не совсем В скобках может находится переменная другого типа, т.е. это присвоение значения переменной + приведение типа в одном флаконе. (В данном примере я не вижу смысла так делать, ну, пусть будет) Далее идёт выбор из глобального перечисления, инициализированного в начале программы. В зависимости от ключа, меняются координаты нашей пустой клетки. Далее если мы не вылазим за пределы доски, пустая клетка меняется местом с каким-либо символом, координаты которого были вычислены выше. Ну и наконец самая главная функция: int main() В ней мы объявляем массив, нашу доску. Инициализируем наш массив, наполняем его символами. Перемешиваем значения. Далее идёт сам игровой цикл, в котором мы: Выводим доску на экран. Выводим подсказку о управляющих символах. Считываем символ, приводим его к типу EMove (это наше перечисление). Делаем ход. Вот такой расклад. Игра продолжается бесконечно, даже если вы выиграете, цикл продолжится. Попозже попробуем создать небольшую доработку этой игры. Во-первых сделаем так, чтобы не надо было постоянно нажимать на Enter. Во-вторых сделаем игровой цикл чувствительным к нашим победам. Можно сделать игру на время.Ну и чего-нибудь ещё добавим.
|