Логическую игру-головоломку "пятнашки" знают все (или почти все).
Поле представляет собой матрицу (двумерный массив) размерностью 4x4,
один из элементов пустой.
Перемешанные цифры, от 1 до 15 помещаются в это поле, задача, выстроить эти цифры в порядке возрастания.
Разберём одну из реализаций этой задачи, код написан на C++.
200?"200px":""+(this.scrollHeight+5)+"px");">
#include
#include
Enum EMove {keUp = "w", keDown = "s", keLeft = "a", keRight = "d"};
// Function declarations
void InitializeBoard(char);
void PrintBoard(char);
void LocateSpace(int&, int&, char );
void Randomize(char);
void Move(char, const EMove);
Int main()
{
char caaBoard;
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)
{
const char kcaaInitial = {
{"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 = kcaaInitial;
}
{
using namespace std;
for (int iRow = 0; iRow < 4; ++iRow)
{
for (int iCol = 0; iCol < 4; ++iCol)
cout << caaBoard;
cout << endl;
}
}
{
for (int iRow = 0; iRow < 4; ++iRow)
for (int iCol = 0; iCol < 4; ++iCol)
if (caaBoard == " ")
{
irRow = iRow;
irCol = iCol;
}
}
{
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;
}
}
}
}
{
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 = caaBoard;
caaBoard = " ";
}
}
Рассмотрим основные функции.
200?"200px":""+(this.scrollHeight+5)+"px");">
void InitializeBoard(char)
Инициализация доски, если дословно.
Суть данной функции заключается в заполнении массива, переданного в параметрах значениями локального массива этой функции.
Массив содержит 16 символов (видимо для краткости
вывода на экран автор представил числа от 10 до 15 в шестнадцатеричном виде) и двойной цикл (так как массив двумерный) переносящий значения массива в тот, который мы укажем в параметрах.
200?"200px":""+(this.scrollHeight+5)+"px");">
void PrintBoard(char caaBoard)
Дословно, печать доски.
Выводит содержимое доски на экран.
Также используется двойной цикл, из-за двумерности массива.
Далее про двойные циклы я буду умалчивать, так как данный факт должен быть вами уже осознан.
200?"200px":""+(this.scrollHeight+5)+"px");">
void LocateSpace(int& irRow, int& irCol, char caaBoard)
Функция возвращает координаты пустой клетки.
Реализована путём поиска пробела (символа) в нашем массиве и возвращение индексов в случае нахождения.
Обратим внимание на то, что первые два параметра указывают на адрес переменных, введённых в параметрах функции.
Далее об этом я расскажу, здесь сделал небольшой акцентик просто.
200?"200px":""+(this.scrollHeight+5)+"px");">
void Randomize(char caaBoard)
Функция перемешивающая значения нашей доски.
Для качественного перемешивания используется функция srand() с параметром (unsigned int)time(0) возвращающим системное время и приведённого к виду без знакового целого числа.
Если данную функцию не использовать то функция rand() будет выдавать одно и то же число.
Также для передвижения клеток на доске используется функция Move , которую мы рассмотрим далее.
200?"200px":""+(this.scrollHeight+5)+"px");">
void Move(char caaBoard, const EMove keMove)
Рассмотрим логику.
Сначала мы определяем положение путой клетки, для этого применяем функцию LocateSpace.
Вот здесь и обратим внимание, что передаётся в эту функцию.
Перед ней объявлены две переменные целого типа, int - это iRowSpace и iColSpace .
В параметрах не указывается никаких дополнительных знаков.
Функция работает с адресами этих переменных, т.е. грубо говоря переменные остаются здесь, а мы переходим к функции, которая находится в другом месте памяти и она меняет значения этих переменных не на прямую, а через их адрес.
(Кстати можете поискать пример работы с указателями в статьях, может будет понятнее)
Далее мы видим интересный фокус с инициализацией вновь объявленных переменных:
200?"200px":""+(this.scrollHeight+5)+"px");">
int iColMove(iColSpace);
int iRowMove(iRowSpace);
в принципе то же, что и
200?"200px":""+(this.scrollHeight+5)+"px");">
int iColMove = iColSpace;
int iRowMove=iRowSpace;
НО, не совсем
В скобках может находится переменная другого типа, т.е. это присвоение значения переменной + приведение типа в одном флаконе.
(В данном примере я не вижу смысла так делать, ну, пусть будет)
Ну и наконец самая главная функция:
int main()
В ней мы объявляем массив, нашу доску.
Инициализируем наш массив, наполняем его символами.
Перемешиваем значения.
Далее идёт сам игровой цикл, в котором мы:
Выводим доску на экран.
Выводим подсказку о управляющих символах.
Считываем символ, приводим его к типу EMove
(это наше перечисление).
Делаем ход.
Вот такой расклад.
Игра продолжается бесконечно, даже если вы выиграете, цикл продолжится.
Попозже попробуем создать небольшую доработку этой игры.
Во-первых сделаем так, чтобы не надо было постоянно нажимать на Enter.
Во-вторых сделаем игровой цикл чувствительным к нашим победам.
Можно сделать игру на время.
Ну и чего-нибудь ещё добавим.
Всем моё си плюс-плюсное здрасьте! Сегодня моя речь пойдет о некоторых полезных функциях языка c++. Когда меня в институте начали учить этому языку программирование я удивился его простоте и возможностям! Но проходили мы только функции, необходимые для написания простеньких консольных приложений. И меня однажды пропёрло покрасить текст и фон в консоли, но учитель забыл их.
Но говорил, что такие функции существуют и не мало! Ну, ясно, что я сразу полез искать в Интернете. И естественно там я ничего не поFINDил. И я уже совсем расстроился, как случилось чудо!!! На уроке информатики, где мы изучали MS Word и DOS , заскучал и полез искать хлам на винте. И случайно увидел какие-то исходники…
Ну естественно я их начал читать. и Тогда я увидел, что есть умные люди в универе, они вручную перевели из Ansi в OEM кодировку и засунули в исходник текст, что позволило работать с русскими словами в с++ и выводить русские буквы в консоль (Об этом позже.). Я подумал "Алелуя! Здесь есть умные люди". И радости моей не было пределе, а того чувака я так и не нашёл:(. Но, запустив этот исходник в билдере я уж посильнее удивился. Он покрасил текст и фон в консольном приложении. Ну, блин, я сразу взялся всё переписывать в тетрадь. И вот теперь я доношу эту инфу до вас!
Текст покрасить можно разными цветами и делаем это так:
#include
#include//Необходимые библиотеки
// В справке указано только КОНИО, но так у меня не работает.
// Ширина экрана (в символах) в DOS всегда 80 (а высота - 25 строк).
textbackground(GREEN); //Цвет фона.
textcolor(RED); //Цвет текста.
clrscr(); //Очищаем экран (Не обязательно!)
Вот привожу таблицу цветов:
BLACK 0
BLUE 1
GREEN 2
CYAN 3
RED 4
MAGENTA 5 //Цвета писать в верхнем регистре а команды в нижнем.
BROWN 6
LIGHTGRAY 7
DARKGRAY 8
LIGHTBLUE 9
LIGHTGREEN 10
LIGHTCYAN 11
LIGHTRED 12
LIGHTMAGENTA 13
YELLOW 14
WHITE 15
BLINK 128
С английским разбирайся сам.
Можно юзать эту функцию со смесью цветов:
textcolor(CYAN + BLINK);
С этой функцией я долбался очень долго!
Теперь перейдем к следующей функции: WINDOW
Она позволяет, как окрасить нужную часть экрана, так и позволить вводить там текст, подобно gotoxy(); или (LOCATE - В бейсике).
Эту функцию нужно юзать с теми же библиотеками, что и textcolor.
clrscr();
textbackground(GREEN);
textcolor(RED);
window(30,10,50,10); //Вот и она.
clrscr();
Результат работы этого сыра смотри сам! И поймешь!
Вот тебе вырезка из справки Билдера
Прототип
void window(int left, int top, int right, int bottom);
С английскими словами проблем быть не должно!
Если window не использовать, то она равна по умолчанию первой строке и первому столбцу.
Эти функции можно использовать несколько раз в программе:
#include
#include
#include
int main()
{ float z,;
clrscr();
textbackground(GREEN);
textcolor(RED); //Тут меняем как надо.
window(30,10,50,10); //Положение экрана!
clrscr();
cout<<"\Hello World!";
getch();
textbackground(BLACK);
textcolor(LIGHTGRAY); //Все возвращаем обратно. По умолчанию так и есть.
clrscr();
}
Теперь ляпну про генератор случайных чисел:
Его юзать вот так:
randomize();
a = rand()/3276 //В a сгенерится любое число от 0 до 9.
a=random(50); //Или вот так число от 0 до 49.
Часто без использования "randomize();" генератор генерит все одинаковые числа!
А с ним все проще он всегда разные числа генерирует.
Всякие прикольные утилиты тоже пригодятся. Например Функция gotoxy(1,44) - отправляет курсор, который мигает в точку окна строки 1 ,столбца 44. A = pow(A,4) - возводим A в 4 степень. Если набираем символы для вывода в консоль: /t - Табуляция. Также чтоб вывеси "/" нужно писать: Cout<<"//"; Вот так вот. Последнее обновление: 20.07.2018 Для вывода информации на консоль мы уже использовали встроенный метод Console.WriteLine
.
То есть, если мы хотим вывести некоторую информацию на консоль, то нам надо передать ее в метод Console.WriteLine: Using System;
namespace HelloApp
{
class Program
{
static void Main(string args)
{
string hello = "Привет мир";
Console.WriteLine(hello);
Console.WriteLine("Добро пожаловать в C#!");
Console.WriteLine("Пока мир...");
Console.WriteLine(24.5);
Console.ReadKey();
}
}
}
Консольный вывод: Привет мир!
Добро пожаловать в C#!
Пока мир...
24,5 Нередко возникает необходимость вывести на консоль в одной строке значения сразу нескольких переменных. В этом случае мы можем использовать прием, который
называется интерполяцией: Using System;
namespace HelloApp
{
class Program
{
static void Main(string args)
{
string name = "Tom";
int age = 34;
double height = 1.7;
Console.WriteLine($"Имя: {name} Возраст: {age} Рост: {height}м");
Console.ReadKey();
}
}
}
Для встраивания отдельных значений в выводимую на консоль строку используются фигурные скобки, в которые заключается встраиваемое значение.
Это можем значение переменной ({name}) или более сложное выражение (например, операция сложения {4 + 7}). А перед всей строкой ставится знак доллара $. При выводе на консоль вместо помещенных в фигурные скобки выражений будут выводиться их значения: Имя: Tom Возраст: 34 Рост: 1,7м Есть другой способ вывода на консоль сразу нескольких значений: Using System;
namespace HelloApp
{
class Program
{
static void Main(string args)
{
string name = "Tom";
int age = 34;
double height = 1.7;
Console.WriteLine("Имя: {0} Возраст: {2} Рост: {1}м", name, height, age);
Console.ReadKey();
}
}
}
Этот способ подразумевает, что первый параметр в методе Console.WriteLine представляет выводимую строку ("Имя: {0} Возраст: {2} Рост: {1}м").
Все последующие параметры представляют значения, которые могут быть встроенны в эту строку (name, height, age). При этом важен порядок подобных
параметров. Например, в данном случае вначале идет name, потом height и потом age. Поэтому у name будет представлять параметр с номером 0 (нумерация начинается
с нуля), height имеет номер 1, а age - номер 2. Поэтому в строке "Имя: {0} Возраст: {2} Рост: {1}м" на место плейсхолдеров {0}, {2}, {1} будут вставляться
значения соответствующих параметров. Кроме Console.WriteLine() можно также использовать метод Console.Write()
, он работает точно так же за тем исключением,
что не осуществляет переход на следующую строку. Кроме вывода информации на консоль мы можем получать информацию с консоли. Для этого предназначен метод Console.ReadLine()
. Он позволяет получить введенную строку. Using System;
namespace HelloApp
{
class Program
{
static void Main(string args)
{
Console.Write("Введите свое имя: ");
string name = Console.ReadLine();
Console.WriteLine($"Привет {name}");
Console.ReadKey();
}
}
}
В данном случае все, что вводит пользователь, с помощью метода Console.ReadLine передается в переменную name. Пример работы программы: Введите свое имя: Том
Привет Том Таким образом мы можем вводить информацию через консоль. Однако минусом этого метода является то, что Console.ReadLine считывает информацию именно в виде
строки. Поэтому мы можем по умолчанию присвоить ее только переменной типа string. Как нам быть, если, допустим, мы хотим ввести возраст в переменную типа int или другую информацию
в переменные типа double или decimal? По умолчанию платформа.NET предоставляет ряд методов, которые позволяют преобразовать различные значения к типам int, double и т.д. Некоторые из этих методов: Convert.ToInt32()
(преобразует к типу int) Convert.ToDouble()
(преобразует к типу double) Convert.ToDecimal()
(преобразует к типу decimal) Пример ввода значений: Using System;
namespace HelloApp
{
class Program
{
static void Main(string args)
{
Console.Write("Введите имя: ");
string name = Console.ReadLine();
Console.Write("Введите возраст: ");
int age = Convert.ToInt32(Console.ReadLine());
Console.Write("Введите рост: ");
double height = Convert.ToDouble(Console.ReadLine());
Console.Write("Введите размер зарплаты: ");
decimal salary = Convert.ToDecimal(Console.ReadLine());
Console.WriteLine($"Имя: {name} Возраст: {age} Рост: {height}м Зарплата: {salary}$");
Console.ReadKey();
}
}
}
При вводе важно учитывать текущую операционную систему. В одних культурах разделителем между целой и дробной частью является точка (США, Великобритания...),
в других - запятая (Россия, Германия...). Например, если текущая ОС - русскоязычная, значит, надо вводить дробные числа с разделителем запятой.
Если локализация англоязычная, значит, разделителем целой и дробной части при вводе будет точка. Пример работы программы: Введите имя: Том
Введите возраст: 25
Введите рост: 1,75
Введите размер зарплаты: 300,67
Имя: Том Возраст: 25 Рост: 1,75м Зарплата: 300,67$ Ввод/вывод
информации в языке C осуществляется с
помощью функций, объявленных в
заголовочных файлах. Простейший способ
воспользоваться функцией – написать
обращение к этой функции. Основные
функции ввода/вывода задаются в
заголовочном файле
stdio.h.
printf()
–
консольный вывод
scanf()
–
консольный ввод.
Структура
обращения к функции:
printf
("строка формата", арг1, арг2, …,
аргN);
В
качестве аргументов функции арг1…аргN
используются
идентификаторы переменных или выражения.
Список аргументов может быть пустым.
Строка формата записывается в двойных
кавычках и может содержать:
любой
текст; спецификаторы форматов (по
количеству аргументов), обозначаются
символом %, содержат информацию о
типе выводимого значения и его
модификации; управляющие символы.
Напомним, что управляющий символ,
или
ESC-последовательность
,
формируется из символа обратной
наклонной черты «\», называемого в языке
C escape-символом, и латинской буквы.
Синтаксис
обращения к функции:
scanf("строка
формата", список аргументов);
С
помощью данной функции производится
ввод с клавиатуры значений переменных,
перечисленных в списке аргументов в
формате, определенном строкой формата.
Функция преобразует последовательность
вводимых символов в различные формы:
целые числа, числа с плавающей точкой,
символы и строки C.
Особенности
функции:
список
аргументов может состоять из одного
или нескольких аргументов; разделителем
в списке аргументов служит [,];
в
качестве аргументов функции используются
только адреса переменных.
Задачей
аргумента в данной функции является
указание адреса ячейки памяти, куда
должно быть помещено вводимое значение.
Так, символ & обозначает операцию
получения адреса переменной, т. е.
конструкция &p обеспечивает
ввод значения в ячейку памяти, где
размещена переменная p. При использовании
функции scanf() необходимо помнить
два правила:
при
считывании значений для переменных
простого типа перед именем переменной
ставится символ &;
при
считывании строки символ & не
ставится, так как строковая переменная
задается с помощью указателя.
Чтение
из потока
при
помощи fgetc
Функция fgetc применяется
для чтения символа из потока.
int
fgetc(FILE *fp);
В
случае успеха, fgetc возвращает
следующий байт или символ из потока
(зависит от того, файл «двоичный» или
«текстовый»). В противном случае, fgetc
возвращает EOF. (Отдельный тип ошибок
можно определить вызовом ferror или
feof с указателем на файл.)
Стандартный макрос getc также
определен в Стандартная
функция getchar также определена
в fwrite
определяется как
int
fwrite (const char * array, size_t size, size_t count, FILE *
stream);
Функция
fwrite
записывает
блок
данных
в
поток
.
Таким
образом запишется массив элементов
array
в
текущую позицию в потоке. Для каждого
элемента запишется
size
байт.
Индикатор позиции в потоке изменится
на число байт, записанных успешно.
Возвращаемое значение будет равно
count
в
случае успешного завершения записи. В
случае ошибки возвращаемое значение
будет меньше count.
Функция
fputc
применяется
для записи символа в поток.
int
fputc(int c, FILE *fp);
Параметр
fputc
конвертируется в unsigned char перед
выводом. Если прошло успешно,
то fputc возвращает записанный
символ. Если ошибка, то fputc возвращает EOF.
Стандартный
макрос putc также определен
в Стандартная
функция putchar, также определенная
в
Cout<<"/n"; можно заменить вот так cout<
Подключив #include math.h мы можем считать различные математические функции:
A = sqrt(A) - Получаем квадратный корень из A;
A = tan, sin, cos, exp, exp2, ctan, ant - всякие тангенсы экспоненты!
Cout << "/t/n";
Есть много нюансов.
/a - Звуковой сигнал.
/v
/f
/x - Разберись с ними сам, а проще попробуй все:.
А ":
Cout<<"/"";
Функция getch(); ждет символа с клавиатуры! Полезна в конце программ, чтобы не закрылось окно.
Функций еще очень много, хочешь знать больше?? Жми F1Консольный вывод
Консольный ввод
61. Файловый ввод/вывод данных в языке Си.