Лицензия Creative Commons
Содержимое блога доступно по лицензии Creative Commons Атрибуция — С сохранением условий
(Attribution-ShareAlike) 3.0 Unported
, если не указано иное.

вторник, 15 марта 2011 г.

Рисуем новогоднюю ёлку с помощью Си: алгоритм номер 2.

После довольно длительного перерыва, продолжаю цикл постов, в которых рассказываю о нестандартном приложении знаний по программированию. В данном случае, я рисую в консоли ель.

В предыдущих постах я:
* Рассказал о том, как как нарисовать равнобедренный треугольник в консоли с помощью звёздочек, и описал проблему создания рисунка, похожего на ель.
* Рассмотрел простой алгоритм отрисовки ёлки, заключающийся в удвоении каждой строки.

Постановка задачи


Последний (и самый, на мой взгляд, интересный) способ отрисовки ели заключается в следующем. Я рисую в строке количество звёздочек, равное номеру строки. Для того, чтобы ель сохраняла некоторую симметричность, я сдвигаю каждую чётную строку вправо на 1 символ.

В итоге, должно получиться следующее:


01      X
02      **
03     ***
04     ****
05    *****
06    ******
07   *******
08   ********
09  *********
10  **********
12      ##
13      ##

Реализация


Для начала, зададим цвет для звезды на верхушке ели, цвет для самой ели и цвет для ствола дерева. Несмотря на то, что программа будет работать в консоли, это не проблема - можно использовать управляющие символы. О них можно прочитать, например, здесь:
http://www.linuxjournal.com/article/8603

Чтобы каждый раз не писать управляющие последовательности для задания цвета строки, я решил поступить следующим образом:

#include <stdio.h>

#define N 15

int main() {
  char c_red[N]    = "\033[49;31;5m"; // Красный
  char c_green[N]  = "\033[49;32;5m"; // Зелёный
  char c_yellow[N] = "\033[49;33;5m"; // Жёлтый
  char c_reset[N]  = "\033[0;0;0m";   // Возврат к первоначальным
                                      //   значениям
  // ...
}

Теперь я могу использовать более осмысленные названия цветов в вызове функции printf(), например:


printf("%sHello %sWorld%s\n", c_yellow, c_green, c_reset); 

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


  int base_width, half_base_width;
  int i, j;

  printf("Please enter height of the fir-tree:\n> ");
  scanf("%d", &base_width);

  half_base_width = base_width / 2;

  for(i = 1; i <= base_width; i++) {
    /* Печатаю номер строки */
    printf("%s%02d\t", c_red, i);

    /* Печатаю одну линию */
    for(j = 1; j < (half_base_width + (i / 2) + 1); j++) {
      if(((i % 2) >  0) && (j <  (half_base_width - (i / 2))) ||
         ((i % 2) == 0) && (j <= (half_base_width - (i / 2))))
        putchar(' ');
      else
        if(i == 1)
          printf("%sX", c_red);  // Большая звезда на верхушке
        else
          printf("%s*", c_green); // Ель
    }
    
    putchar('\n');
  }

Для повышения реалистичности, в завершении отрисовки ёлки нарисую ещё ствол дерева.


  for(i = base_width + 1; 
      i < (base_width + base_width / 10 * 3); i++) {
    printf("%s%02d\t", c_red, i);
    for(j = 0; j <= half_base_width + (base_width / 10) / 2; j++)
      if(j < half_base_width - (base_width / 10))
        putchar(' ');
      else
        printf("%s#", c_yellow);
    putchar('\n');
  }

Значение (base_width + base_width / 10 * 3) используется для задания высоты ствола в зависимости от высоты ели. Значение half_base_width + (base_width / 10) / 2 и half_base_width - (base_width / 10) используется для задания ширины ствола.

В итоге, ель у меня выглядит вот так: