Пятница, 18.07.2025, 01:27
Приветствую Вас Гость | RSS
Главная | Каталог статей | Регистрация | Вход
.:: Меню ::.
.:: Категории каталога ::.
Разное [5]
Различные темы по программированию
Пакет SWT [4]
Практикуемся в написании оконных приложений на Java
Среды разработки, компиляторы и т.п [3]
Сравнения, описания, плюсы и минусы сред разработки. Сравнение компиляторов.
Java [8]
Объектно-ориентированные соображения.
Си++ [19]
Коротко и ясно
Ассемблер [6]
Машинные коды, побитно :)
Форма входа
.:: Поиск ::.
.:: Дополнительно ::.
    Хостинг от Loqo.ru
             .:: Коментируем ::.
Главная » Статьи » Текстовый материал » Си++

Игра - "пятнашка", консоль, Си++.
Специально для русскоязычного населения буду описывать зарубежные примеры на русском языке.
Логическую игру-головоломку "пятнашки" знают все (или почти все).
Поле представляет собой матрицу (двумерный массив ) размерностью 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;

НО, не совсем smile
В скобках может находится переменная другого типа, т.е. это присвоение значения переменной + приведение типа в одном флаконе.
(В данном примере я не вижу смысла так делать, ну, пусть будет)

Далее идёт выбор из глобального перечисления, инициализированного в начале программы.
В зависимости от ключа, меняются координаты нашей пустой клетки.

Далее если мы не вылазим за пределы доски, пустая клетка меняется местом с каким-либо символом, координаты которого были вычислены выше.

Ну и наконец самая главная функция:
int main()

В ней мы объявляем массив, нашу доску.
Инициализируем наш массив, наполняем его символами.
Перемешиваем значения.
Далее идёт сам игровой цикл, в котором мы:
Выводим доску на экран.
Выводим подсказку о управляющих символах.
Считываем символ, приводим его к типу EMove (это наше перечисление).
Делаем ход.

Вот такой расклад.
Игра продолжается бесконечно, даже если вы выиграете, цикл продолжится.
Попозже попробуем создать небольшую доработку этой игры.
Во-первых сделаем так, чтобы не надо было постоянно нажимать на Enter.
Во-вторых сделаем игровой цикл чувствительным к нашим победам.
Можно сделать игру на время.
Ну и чего-нибудь ещё добавим. smile

Категория: Си++ | Добавил: C0demaker (08.05.2009)
Просмотров: 6906 | Рейтинг: 0.0/0
Всего комментариев: 0

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Ant1 © 2025