Страницы

четверг, 27 августа 2015 г.

Черепашкина графика

Черепашья графика, траектория черепахи – это программирование графики с помощью системы односимвольных команд. Эта система похожа на систему управления плоттером, точнее, его пишущим узлом. Есть команды поднять перо, опустить, развернуться в нескольких направлениях, передвинуться на определенное расстояние и так далее. Элементы такого варианта графики имеются в QBasic. Программировать и запоминать все эти команды управления черепашкой довольно муторно, недаром фирма Borland  отказалась от такой графики еще на стадии Turbo Pascal 3.0.
 Реализуем самый простой вариант «черепашьей» графики на С++ в консольном виде (текстовый режим). В нем всего 7 команд и поле передвижения 20*20 клеток. Все это дело можно, конечно, расширить, увеличить функциональность, перенести на другую платформу, там еще допилить что-нибудь. Не знаю, кому это надо, но, мало ли, кого торкнет, будет, чем заняться. А я пока сделаю самое простое.
Вот как примерно выглядит само задание (взято из книги «Как программировать на С++» Х.М.Дейтел, П.Дж.Дейтел).
У нас есть черепаха, которая ползает по комнате, управляемая командами. Черепаха несет пишущее перо, которое может держать поднятым (тогда ничего не рисуется) или опущенным (тогда рисуется каждый шаг черепашки). Она может поворачиваться налево и направо.
Вот список команд:
  • 1 – перо внизу
  • 2 – перо вверху
  • 3 – поворот направо
  • 4 – поворот налево
  • 5, 12 – движение вперед на 12 шагов (или на любое другое)
  • 6 - печать массива позиций
  • 9 - конец данных
Все позиции черепашки будем хранить в массиве 20*20 элементов.
Все команды будем хранить в массиве 100*2, то есть можно внести 100 команд максимум. Можно было бы хранить в одномерном массиве, но так удобнее записывать передвижения – сразу и команда, и количество шагов. Кроме того, возможно добавить такие же двойные команды.
Можно было бы просто считывать с экрана команды и выполнять. Но такой вариант удобней, кмк, можно передавать массив в функцию и там обрабатывать. Впрочем, я делаю без использования функций. Будет много циклов и свитчей.
Нужно еще хранить текущие координаты черепахи, направление движения по х и у, позицию пера. Если черепаха движется с опущенным пером, то нужно ставить 1 в соответствующий элемент массива позиций. Если перо поднято, то ничего ставить не надо, просто определить новые координаты черепахи.
Животное начинает свое движение с позиции 0, 0. Начало системы координат находится в левом верхнем углу. При движении направо х увеличивается, направление положительное (+1), налево – х уменьшается, направление равно -1. При движении по вертикали направление х равно нулю.
При движении вниз увеличивается у, направление у равно +1, при движении верх наоборот. При движении по горизонтали направление у равно 0.
Нужно иметь в виду, что мы храним координате в массиве. То есть за х отвечают номера столбцов, а за у – строк. При выводе массива на экран нужно делать его перевернутым.
При печати массива все элементы, равные 0, выводятся на экран как пробелы. Элементы, равные 1, выводятся как звездочки *.

Общая схема работы программы.
Инициализация массивов и переменных.
Запрос у пользователя команд и запись их в массив. Если задана команда 5 (передвижение), то дополнительно запрашивается число шагов.
Обработка массива команд и выполнение соответствующих действий.

Текст программы с комментариями:
#include<iostream>
using namespace std;
int main()
{
    //размеры массивов
    const int FloorSize = 20;
    const int CommandSize = 100;
    //массив координат черепашки
    int Floor[FloorSize][FloorSize];
    //массив команд
    int c[CommandSize][2];
    //к - команда, i,j - счетчики цикла, poz - счетчик массива команд
    int k, i, j, poz = 0;
    //направления по х и у
    //текущие координаты по х и у
    //новые координаты по х и у
    //положение пера - 0 - поднято, 1 - опущено
    int dirX =1, dirY = 0, curX = 0, curY = 0, newX = 0, newY = 0, p=0;
    //переменные для циклов
    int x0, x1, a;
    //инициализация массивов нулями
    for(i=0; i<FloorSize; i++)
        for(j=0; j<FloorSize; j++)
            Floor[i][j]=0;
    for(i=0; i<CommandSize; i++)
        for(j=0; j<CommandSize; j++)
            c[i][j]=0;
    //главный цикл программы
    //пока не введена цифра 9
        //или не заполнен массив команд
    do
    {
        //Ввод команды пользователем
        cout<<"Enter the command"<<endl;
        cin>>k;
        switch(k)
        {
            //заполняем массив команд
        case 1: case 2: case 3: case 4: case 6: case 9:
            {
                c[poz][0] = k;
            }
            break;
            //команда на передвижение
            //запрос и ввод количества шагов
        case 5: {
            c[poz][0] = k;
            cout<<"Enter the number of steps: ";
            cin>>k;
            c[poz][1] = k;
            }
            break;
            //прочие команды не записываем в массив
            //переходим к следующей итерации
        default: {
            cout<<"Command is not defined\n";
            continue;
        }
            break;
        }
        //переход к следующей позиции в массиве команд
        poz++;
    } while ((k!=9)&&(poz<CommandSize));

    //работа черепашки по заданной программе

    for(a=0; a<CommandSize; a++)
    {
        switch(c[a][0])
        {
            //подъем пера
            case 1: p = 0;
                break;
            //опускаем перо
            case 2: p = 1;
                break;
            //поворот направо, меняем направление
            case 3:
            {
                j = dirY;
                dirY = dirX;
                dirX = -j;
            }
            break;
            //поворот налево, меняем направление
            case 4:
            {
                j = dirY;
                dirY = -dirX;
                dirX = j;
            }
            break;
            //передвижение
            case 5:
            {
                //новые координата х
                newX = curX + c[a][1]*dirX;
                //если есть выходы за пределы массива,
                //то устанавливаем крайние значения
                if(newX >= FloorSize)
                {
                    newX = FloorSize-1;
                }
                else
                    if (newX < 0)
                    {
                        newX = 0;
                    }
                //новая координата у
                newY = curY + c[a][1]*dirY;
                //выходы за предела массива
                if(newY >= FloorSize)
                {
                    newY = FloorSize-1;
                }
                else
                    if (newY < 0)
                    {
                        newY = 0;
                    }
               
                //если перо  опущено, то заполняем
                //массив позиций, иначе пропускаем это действие
                if(p)
                {

                //определяем начальное и конечное значение
                    //счетчика цикла в зависимости от направления движения
                    //движение вправо
                if(dirX==1) {
                        x0 = curX;
                        x1 = newX;
                }
                else
                {
                    //движение влево
                    if(dirX == -1)
                    {
                            x0 = newX;
                            x1 = curX;
                    }
                    //движение по вертикали, х не меняется
                    else x0 = x1 = curX;
                }
                //заполняем массив позиций
                    for(i = x0; i <=x1; i++)
                        Floor[i][curY] = p;
                //движение вниз
                if(dirY==1) {
                        x0 = curY;
                        x1 = newY;
                }
                else
                {
                    //движение вверх
                    if(dirY == -1)
                    {
                            x0 = newY;
                            x1 = curY;
                    }
                    //движение по горизонтали, у не меняется
                    else x0 = x1 = curY;
                }
                //заполняем массив позиций черепашки
                for(i = x0; i <=x1; i++)
                    Floor[curX][i] = p;   
                } //конец заполнения текущего движения
                //определяем текущие координаты на следующий шаг
                curX = newX;
                curY = newY;
            }
            break;
            case 6:
            {
                //вывод массива позиций на экран
                //выводим в перевернутом виде
                //потому что у нас координаты хранятся в массиве,
                //то есть позиция по х определяется номером столбца, а не строки
                for(x0= 0; x0 < FloorSize; x0++)
                    {
                        for(x1 = 0; x1 < FloorSize; x1++)
                            cout<<((Floor[x1][x0]==1)?'*':' ');
                        cout<<endl;
                    }
            }
            break;
            default:
                break;
        }
        //если окончание ввода команд, то прекращаем цикл досрочно
        if(c[a][0] == 9) break;
    }
    cin.get();
    return 0;
}
Результаты работы программы.
Квадрат 12 на 12

Черепашья графика - рисование квадрата

Буква Г

Черепашья графика - рисование буквы

Можно увеличить число команд, добавить считывание массива команд из файла, увеличить массив позиций и так далее.