Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Игра «куча»  (Прочитано 12779 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
IgnisFatuus
Постоялец

ru
Offline Offline

« : 12-07-2012 05:01 » 

Наверное я уже достал всех, да? Улыбаюсь 


Вот есть задача - Игра «куча». Человек и компьютер по очереди делают ходы, забирая из кучи, состоящей из Nпредметов, от 1 до K штук. Проигрывает тот, кто забрал последний предмет. Варианты:
a)Компьютер «берет» случайное количество предметов.
b)Найти для компьютера оптимальную стратегию.

Набросал код.

Код: (C)
#include <stdio.h>
#include <conio.h>

int main(){
        int N = 10;
        int k = 3;
        int i=0;
        int n=1;
        int take;

        for (i=0; N>1; i++){
                printf("Items - > %d\n", N);
                printf ("Turns =  %d, how much do you wanna take? (1-3)", n);
                scanf ("%d", &take);
                n++;
                N -= take;

                printf("Items left - > %d\n", N);

                printf("Turns =  %d, computer takes %d ", n, k = rand()%3);
                N -= k;
                printf("Items left - > %d\n", N);
                n++;
                if (N<=3){
                        printf("%s is the winner! Congratulations, %s", n%2==0? "Computer" : "Player" );}

        }

        getch();
И как в том мультике - у меня встал вопрос. И не один.
1) Проверка победителя - я хотел реализовать таким образом:

каждый ход накручивается счетчик (n++) и когда предметов останется скажем меньше 3 или 5 (ясно, что взяв 2-3 предмета можно выиграть если очередь - твоя) глянуть на счетчик - делится ли он по модулю на 2. Т.е. МОЙ ход - он ВСЕГДА не четный. У компа - наоборот. У меня алгоритм не пашет. Что я упустил ?

2) Что, если я введу не 1-3 а сразу 9? Можно ли написать проверку в таком ключе -
Код: (C)
scanf ("%d", &take)!=1 || take <1 || take>3  || (как - то запилить проверку на буквы-символы - как?)
printf("You fu**ing kidding me!!!11 Don't want to pay with you any more!", break;


3) Что-то еще, но я с утра сонный, хз, не помню...

4) Есть поправки? Сразу говорю - буду рад критике.
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 12-07-2012 05:50 » 

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

Процедурно нужно писать - будет понятнее. А если ещё и с полиморфизмом - красивее.

И вообще такие вещи начинают писать с алгоритма, а не кода.
« Последнее редактирование: 12-07-2012 05:52 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #2 : 12-07-2012 06:03 » 

Угу, про проверку понял

IgnisFatuus,
Процедурно нужно писать - будет понятнее. А если ещё и с полиморфизмом - красивее.


Раскроешь мысль?
Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 12-07-2012 07:06 » 

IgnisFatuus, первым делом алгоритм.

Процедуры же - это средство декомпозиции задачи. Полиморфизм - это, образно выражаясь, подобие в разнообразии.

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

Какие такие элементарные действия у игроков? Это в общем-то одно: сказать, сколько камней забирает игрок. Но каждый игрок делает это по-своему: человек вводит с клавиатуры, компьютер - вычисляет.

Отдельная операция - определение победителя. Раз она должна дважды повторяться, её тоже целесообразно вынести в отдельную функцию.

Затем нужно определить интерфейсы.

Допустим, функция, которая опрашивает игрока. От неё требуется количество камней - целое число, и ей не требуется ничего. Тогда её сигнатура может выглядеть так:
Код: (C++)
int how_many_stones();

Проверка конца игры. Чтобы её выполнить, нужно знать количество оставшихся камней - целое число. В результате она должна сказать, закончилась игра или нет - логическое значение. Тогда её сигнатура может выглядеть так:
Код: (C++)
bool check_win(int stones_quantity);

Нужно учесть, что функции опроса игроков хоть и одинаковы по интерфейсу, но различны по содержанию. Договоримся, что каждый вид функции будет иметь суффикс в виде типа игрока: human - для человека, ai - для искусственного интеллекта.

Тогда общая схема главного цикла будет выглядеть примерно так:
Код: (C++)
int stones_quantity = ...
for(;;)
{
  ... = how_many_stones_human();
  if(check_win(stones_quantity) {
    ...
    break;
  }
  ... = how_many_stones_ai();
  if(check_win(stones_quantity) {
    ...
    break;
  }  
}

Что здесь бросается в глаза? Повтор по сути одной и той же конструкции. Чтобы этого избежать, как раз можно использовать полиморфизм. Но для этого придётся познакомиться с указателем на функцию - такой переменной, которая ссылается на функцию. Определим тип такого указателя:
Код: (C++)
typedef int (*how_many_stones_type)();
Определим таблицу-селектор для полиморфных функций и заполним её функциями:
Код: (C++)
how_many_stones_type how_many_stones[] = {
  how_many_stones_human,
  how_many_stones_ai
};

Теперь, воспользовавшись циклическим переключателем, можно в каждой итерации цикла выбирать то одну, то другую функцию, чередуя ходы игроков. Общая схема главного цикла сразу сжимается:
Код: (C++)
int stones_quantity = ...
int player_selector = 0;
do
{
  ... = how_many_stones[player_selector]();
  if(player_selector == 1)
  {
    player_selector = 0;
  }
  else
  {
    player_selector += 1;
  }
}
while(!check_win(stones_quantity));
...
« Последнее редактирование: 12-07-2012 15:57 от Алексей1153++ » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #4 : 12-07-2012 08:18 » 

Все дело в том, что код задали на Си.

И с тем прицелом, что функции мы пока не знаем.

Добавлено через 1 минуту и 16 секунд:
Точнее я самостоятельно учу С++ (как раз разбираю функции, их перегрузку и т.п.), но на данном этапе препод дал задачу обходиться увеличительным стеклом вместо микроскопа. Как-то так.

Добавлено через 58 минут и 30 секунд:
В итоге получилось (каменным топором и дубиной)
Код: (C)
#include <stdio.h>
#include <conio.h>
#include <windows.h>


int main(){

        int N = 10;
    int k = 3;
    int i=0;
    int n=1;
    int take;

        for (i=0; N>1; i++){
                printf ("Turns =  %d, how much do you wanna take? (1-3)", n);
        scanf ("%d", &take);
        if (take > 3 || take <1){ printf("You kidding me??? You entered wrong value! I don't want to pay with you any longer!");
        return;}
        n++;
        N -= take;
                if (N<=4 && N>=2){
                        printf ("Than computer takes %d   :-)\n", N-1);
                        printf("You have lost!\n");
                        return;
                }
               
       
                printf("Items left - > %d\n", N);
                Sleep(1000);
                if (k > N)k = rand()%3;
                else if (N <=3) k = N-1;
                printf("Turns =  %d, computer takes %d \n", n, k);
        N -= k;
        printf("Items left - > %d\n", N);
        n++;
                if (N<=4 && N>=2){
                        printf("Take %d and that's all!", N-1);
                        printf("You are a winner! Congratulations!\n");
                        return;
                }

Как вам?
« Последнее редактирование: 12-07-2012 09:18 от IgnisFatuus » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 12-07-2012 10:08 » 

IgnisFatuus, хотя бы форматировать код можно. А то что это - всё вкривь и вкось? Топор тут ни при чём. Можно и без процедур писать, чётко структурируя код.

Код: (C)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define STONES 10

#define MIN_PART 1
#define MAX_PART 3

#define FLAG_HUMAN 1
#define FLAG_AI 2

int main()
{
        int stones_quantity = STONES;
        int max_part;
        int taken_stones;
        int control_flag = FLAG_HUMAN;

        srand((unsigned int)time(NULL));
        for(;;)
        {
                printf("There are %i stones.\n", stones_quantity);
                max_part = stones_quantity >= MAX_PART ? MAX_PART : stones_quantity;
                if(control_flag == FLAG_HUMAN)
                {
                        printf("You should take not less than %i and not more than %i stones.\n", MIN_PART, max_part);
                        printf("How many stones you want to take? ");
                        scanf("%d", &taken_stones);
                        if(taken_stones <= MIN_PART || taken_stones >= max_part)
                        {
                                printf("You said incorrect quantity.\n");
                                break;
                        }
                }
                if(control_flag == FLAG_AI)
                {
                        taken_stones = stones_quantity - 1;
                        if(taken_stones > max_part || taken_stones < MIN_PART)
                        {
                                taken_stones = rand() % (max_part - MIN_PART + 1) + MIN_PART;
                        }
                        printf("Computer took %i stones.\n", taken_stones);
                }
                stones_quantity -= taken_stones;
                if(stones_quantity == 0)
                {
                        if(control_flag == FLAG_HUMAN)
                        {
                                printf("Computer wins and you loses.\n");
                        }
                        if(control_flag == FLAG_AI)
                        {
                                printf("You wins and computer loses.\n");
                        }
                        break;
                }
                else
                {
                        if(control_flag == FLAG_HUMAN)
                        {
                                control_flag = FLAG_AI;
                        }
                        else
                        {
                                control_flag = FLAG_HUMAN;
                        }
                }
        }
        return 0;
}
Написано не глядя, не запускал, не проверял. Могут быть ошибки. Дело не в этом, дело в оформлении и структуре кода.

У тебя проверки хаотично раскиданы по коду, одни и те же вещи повторяются. Я почему про функции говорил? Не потому, что они очень нужны при написании, а только потому, что их систематическое применение наводит порядок в голове и коде. Написанный с помощью функций код можно переписать и без функций, используя только управляющие конструкции.
« Последнее редактирование: 12-07-2012 12:12 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #6 : 12-07-2012 10:33 » 

Когда остается 1 камень AI берет 0.

Добавил
Код: (C)
else if (taken_stones<=0)
{
        taken_stones =1;
}

Работает.
« Последнее редактирование: 12-07-2012 10:40 от IgnisFatuus » Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #7 : 12-07-2012 12:12 » 

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

Здесь главное - блочная структура. Каждый блок имеет входы, выходы и внутренние переменные. Все блоки связаны в цепочки, какие-то входы следующих привязаны к каким-то выходам предыдущих, и каждый блок решает какую-то небольшую задачу, и все части решения этой задачи сосредоточены только в смежных строчках кода, относящихся к блоку. Из блоков не нужно делать спагетти, перемешивая их части, хотя бы порядок исполнения строк кода и допускал такое перемешивание, не вредя результату. Спагетти совершенно не работает в больших программах - таких, которые нельзя написать "на одном дыхании", над которыми нужно работать долго, или над которыми работают разные люди. Декомпозиция позволяет свести сложные задачи к простым - таким, которые пишутся за раз. Каждая элементарная задача - это либо написание элементарного блока кода, либо комбинация блока из других блоков. Других задач в кодировании нет. Функции же позволяют оформить блоки явным образом. Без функций можно писать комментарии.

Например, так:
Код: (C)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define STONES 10

#define MIN_PART 1
#define MAX_PART 3

#define HUMAN_MOVE 1
#define AI_MOVE 2

int main()
{
        /* БЛОК 0 "ОБЩИЙ"
         * Входные значения:
         *      STONES,
         *      MIN_PART,
         *      MAX_PART,
         *      HUMAN_MOVE_HUMAN,
         *      AI_MOVE
         * Выходные значения: нет.
         * Внутренние переменные:
         *      stones_quantity,
         *      taken_stones,
         *      move,
         *      (неявно) состояние генератора ПСЧ.
         * Примечания:
         *      max_part на уровне блока 0 не используется, её значение безразлично.
         * НАЧАЛО 0
         * БЛОК 1 "ИНИЦИАЛИЗАЦИЯ"
         * Входные значения:
         *      STONES,
         *      HUMAN_MOVE,
         *      AI_MOVE
         * Выходные значения:
         *      stones_quantity
         *      move
         *      (неявно) состояние генератора ПСЧ.
         * Внутренние переменные: нет.
         * Примечания:
         *      taken_stones не инициализируется, не может быть входным значением следующего блока.
         * НАЧАЛО 1
         */

        int stones_quantity = STONES;
        int max_part;
        int taken_stones;
        /* Выбор права первого хода может быть и сложнее: по выбору пользователя или по жребию. */
        int move = HUMAN_MOVE;

        srand((unsigned int)time(NULL));
        /* КОНЕЦ 1 */
        for(;;)
        {
                /* БЛОК 2 "ПРИНЯТИЕ РЕШЕНИЯ ИГРОКОМ"
                 * Входные значения:
                 *      MIN_PART,
                 *      MAX_PART,
                 *      HUMAN_MOVE,
                 *      AI_MOVE
                 *      stones_quantity,
                 *      move,
                 *      (неявно) состояние генератора ПСЧ.
                 * Выходные значения:
                 *      taken_stones
                 *      (неявно) состояние генератора ПСЧ.
                 * Внутренние переменные:
                 *      max_part
                 * Примечания:
                 *      Весь блок предназначен для инициализации taken_stones.
                 * НАЧАЛО 2
                 */

                printf("There are %i stones.\n", stones_quantity);
                /* БЛОК 2.1 "ОГРАНИЧЕНИЕ РЕШЕНИЯ ПОЛЬЗОВАТЕЛЯ"
                 * Входные значения:
                 *      MAX_PART,
                 *      stones_quantity
                 * Выходные значения:
                 *      max_part
                 * Внутренние переменные: нет.
                 * Примечания:
                 *      Весь блок предназначен для инициализации max_part.
                 *      move не используется, значение безразлично.
                 *      (неявно) состояние генератора ПСЧ не используется, значение безразлично.
                 * НАЧАЛО 2.1
                 */

                max_part = stones_quantity >= MAX_PART ? MAX_PART : stones_quantity;
                /* КОНЕЦ 2.1
                 * БЛОК 2.2 "ПРИНЯТИЕ РЕШЕНИЯ"
                 * Входные значения:
                 *      MIN_PART,
                 *      max_part,
                 *      HUMAN_MOVE,
                 *      AI_MOVE,
                 *      move,
                 *      stones_quantity,
                 *      (неявно) состояние генератора ПСЧ.
                 * Выходные значения:
                 *      taken_stones
                 *      (неявно) состояние генератора ПСЧ.
                 * Внутренние переменные: нет.
                 * Примечания:
                 *      Каждый альтернативный внутренний блок должен установить значение taken_stones.
                 * НАЧАЛО 2.2
                 */

                switch(move)
                {
                case HUMAN_MOVE:
                        /* БЛОК 2.2.1 "ПРИНЯТИЕ РЕШЕНИЯ ПОЛЬЗОВАТЕЛЕМ"
                         * Входные значения:
                         *      MIN_PART,
                         *      max_part
                         * Выходные значения:
                         *      taken_stones
                         * Внутренние переменные: нет.
                         * Примечания:
                         *      move на уровне 2.2.* не используется, её значение безразлично.
                         *      (неявно) состояние генератора ПСЧ не используется, значение безразлично.
                         * НАЧАЛО 2.2.1
                         */

                        printf("You should take not less than %i and not more than %i stones.\n", MIN_PART, max_part);
                        printf("How many stones you want to take? ");
                        scanf("%d", &taken_stones);
                        if(taken_stones < MIN_PART || taken_stones > max_part)
                        {
                                /* Если пользователь нарушил правило, завершаем работу программы. */
                                printf("You said incorrect quantity.\n");
                                return 0;
                        }
                        break;
                        /* КОНЕЦ 2.2.1 */
                case AI_MOVE:
                        /* БЛОК 2.2.2 "ПРИНЯТИЕ РЕШЕНИЯ КОМПЬЮТЕРОМ"
                         * Входные значения:
                         *      MIN_PART,
                         *      max_part,
                         *      stones_quantity,
                         *      (неявно) состояние генератора ПСЧ.
                         * Выходные значения:
                         *      taken_stones,
                         *      (неявно) состояние генератора ПСЧ.
                         * Внутренние переменные: нет.
                         * Примечания:
                         *      move на уровне 2.2.* не используется, её значение безразлично.
                         * НАЧАЛО 2.2.2
                         */

                        /* Выбираем оптимальное количество камней для победы. */
                        taken_stones = stones_quantity - 1;
                        if(taken_stones < MIN_PART || taken_stones > max_part)
                        {
                                /* Если оптимальное количество нарушает правило, выбираем любое значение среди разрешённых.  */
                                taken_stones = rand() % (max_part - MIN_PART + 1) + MIN_PART;
                        }
                        /* Выбранное значение всегда соответствует правилам и, возможно, является оптимальным. */
                        printf("Computer took %i stones.\n", taken_stones);
                        break;
                        /* КОНЕЦ 2.2.1 */
                }
                /* КОНЕЦ 2.2
                 * КОНЕЦ 2
                 * БЛОК 3 "ХОД"
                 * Входные значения:
                 *      stones_quantity,
                 *      taken_stones
                 * Выходные значения:
                 *      stones_quantity
                 * Внутренние переменные: нет.
                 * Примечания:
                 *      move не используется, значение безразлично.
                 *      (неявно) состояние генератора ПСЧ не используется, значение безразлично.
                 * НАЧАЛО 3
                 */

                stones_quantity -= taken_stones;
                /* КОНЕЦ 3
                 * БЛОК 4 "ОПРЕДЕЛЕНИЕ РЕЗУЛЬТАТА ИГРОКА"
                 * Входные значения:
                 *      stones_quantity,
                 *      move
                 * Выходные значения: нет.
                 * Внутренние переменные: нет.
                 * Примечания:
                 *      taken_stones не используется, значение безразлично.
                 *      (неявно) состояние генератора ПСЧ не используется, значение безразлично.
                 * НАЧАЛО 4
                 */

                if(stones_quantity == 0)
                {
                        /* Если камней не осталось, чей ход был последним - проиграл. Программа завершается.
                           Если камни остались, результат не определён, программа продолжает работу. */

                        switch(move)
                        {
                        case HUMAN_MOVE:
                                printf("Computer wins and you lose.\n");
                                break;
                        case AI_MOVE:
                                printf("You win and computer loses.\n");
                                break;
                        }
                        return 0;
                }
                /* КОНЕЦ 4
                 * БЛОК 5 "ПЕРЕДАЧА ХОДА"
                 * Входные значения:
                 *      move
                 * Выходные значения:
                 *      move
                 * Внутренние переменные: нет.
                 * Примечания:
                 *      stones_quantity не используется, значение безразлично.
                 *      taken_stones не используется, значение безразлично.
                 *      (неявно) состояние генератора ПСЧ не используется, значение безразлично.
                 * НАЧАЛО 5
                 */

                switch(move)
                {
                case HUMAN_MOVE:
                        move = AI_MOVE;
                        break;
                case AI_MOVE:
                        move = HUMAN_MOVE;
                        break;
                }
                /* КОНЕЦ 5 */
        }
        /* КОНЕЦ 0 */
}
« Последнее редактирование: 12-07-2012 17:18 от Dimka » Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 12-07-2012 16:36 » 

b)Найти для компьютера оптимальную стратегию.

И где она, эта оптимальная стратегия? Сразу пошли обрывки кода, без попытки осмыслить задачу. А это ничем хорошим никогда не кончается.
Рассмотрим сначала для простоты вариант, когда игроки могут брать от 1 до 9 предметов. Тогда первый игрок (A) должен взять столько предметов, чтобы в куче осталось (10*k+1) предметов. На каждый ход игрока B, взявшего n предметов, A берет (10-n) предметов, снова оставляя в куче (10*k+1). В конце концов игроку B придется взять последний предмет и проиграть.
Например, в куче 37 предметов. Тогда беспроигрышная стратегия для A такова:

ИгрокВзялОсталось
A
6
31
B
7
24
A
3
21
B
4
17
A
6
11
B
8
3
A
2
1

Разумеется, все это не сработает, если в куче изначально (10*k+1) предметов. В этом случае хитрый A должен либо уступить право первого хода наивному противнику B, либо уповать на то, что тот не знает выигрышной стратегии, и тогда при своем ходе выровнять кучу на (10*k+1). Если ни один из этих трюков не сработает, A гарантированно проиграл.
Обобщить на случай взятия произвольного числа предметов попробуйте самостоятельно, это вовсе не сложно.
Как видите, задача элементарно решается без персептронов с полиморфизмами, достаточно нескольких строк даже на Бейсике. Если немного подумать в самом начале работы, можно избавить себя от массы совершенно ненужного труда впоследствии.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
IgnisFatuus
Постоялец

ru
Offline Offline

« Ответ #9 : 13-07-2012 07:45 » new

Dimka, Вы не правы на счет моего ответа. Я вчера потратил довольно много времени обдумывая Ваш код - алгоритм, стилистику написания и пр.  Я к сожалению не видел Ваш последний пост вчера - курсы закончились в 22:00 и пока я добрался до дома было уже 23:40 - не до компа.
Я с Вашего позволения покажу Ваш код и Ваши комментарии сегодня на занятиях - думаю всем будет полезно. Вы не против?

Уважаемый r̶e̶s̶c̶u̶e̶ ̶r̶a̶n̶g̶e̶r̶ Dale! Большое спасибо, буду траить этот алгоритм. Есть пара мыслей как перенести его на несколько нужных программ. Большое спасибо еще раз.

Записан
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 13-07-2012 11:22 » 

Цитата: IgnisFatuus
Вы не против?
Не против.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Dimka
Деятель
Команда клуба

ru
Offline Offline
Пол: Мужской

« Ответ #11 : 13-07-2012 13:08 » 

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

Код: (C)
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define STONES 10
#define MIN_PART 1
#define MAX_PART 3

#define HUMAN_MOVE 0
#define AI_MOVE 1

void initialize(int *stones_quantity, int *move) {

  srand((unsigned int)time(NULL));
  *stones_quantity = STONES;
  *move = rand() % 2 == 0 ? HUMAN_MOVE : AI_MOVE;

}

void limit(int stones_quantity, int *max_part) {

  *max_part = stones_quantity < MAX_PART ? stones_quantity : MAX_PART;

}

void select_human(int max_part, int *taken_stones) {

  printf("You should take %i-%i stones.\n", MIN_PART, max_part);
  printf("How many stones you take? ");
  scanf("%d", taken_stones);
  if(*taken_stones < MIN_PART || *taken_stones > max_part) {
    printf("You said incorrect number of stones.\n");
    exit(0);
  }

}

void select_ai(int max_part, int stones_quantity, int *taken_stones) {

  *taken_stones = stones_quantity % MAX_PART;
  if(*taken_stones > 1) {
    *taken_stones -= 1;
  }
  else {
    *taken_stones = MAX_PART + *taken_stones - 1;
  }
  if(*taken_stones < MIN_PART || *taken_stones > max_part) {
    *taken_stones = rand() % (max_part - MIN_PART + 1) + MIN_PART;
  }
  printf("Computer took %i stones.\n", *taken_stones);

}

void select(int move, int stones_quantity, int *taken_stones) {

  int max_part;

  limit(stones_quantity, &max_part);
  printf("There are %i stones in heap.\n", stones_quantity);
  switch(move) {
  case HUMAN_MOVE:
    select_human(max_part, taken_stones);
    break;
  case AI_MOVE:
    select_ai(max_part, stones_quantity, taken_stones);
    break;
  }

}

void make_move(int taken_stones, int *stones_quantity) {

  *stones_quantity -= taken_stones;

}

void check(int move, int stones_quantity) {

  if(stones_quantity == 0) {
    switch(move) {
    case HUMAN_MOVE:
      printf("Computer wins and you lose.\n");
      break;
    case AI_MOVE:
      printf("You win and computer loses.\n");
      break;
    }
    exit(0);
  }

}

void pass(int *move) {

  switch(*move) {
  case HUMAN_MOVE:
    *move = AI_MOVE;
    break;
  case AI_MOVE:
    *move = HUMAN_MOVE;
    break;
  }

}

void main() {

  int stones_quantity;
  int taken_stones;
  int move;

  initialize(&stones_quantity, &move);
  for(;;) {
    select(move, stones_quantity, &taken_stones);
    make_move(taken_stones, &stones_quantity);
    check(move, stones_quantity);
    pass(&move);
  }

}

Комментарии о блоках становятся излишни. Но что характерно для этого кода по сравнению с предыдущим. Если предыдущий можно читать последовательно - как роман, такой разбитый на функции код уже последовательно читать не получится. Скорее он похож на справочник, и чтобы разбираться в такой программе, нужны закладки - требуется переходить туда-сюда, изучая алгоритм. Зато каждая функция помещается даже на маленьком экране целиком и может быть воспринята как одно целое.

Что ещё характерного для этого кода? Что пользовательский интерфейс (вывод надписей на экран, ввод пользователя), а также завершение работы программы оказываются разбросанными по всему коду. Таким образом, упорядочив и структурировав действия алгоритма, мы потеряли сгруппированность в каком-то одном месте таких аспектов программы, как пользовательский интерфейс и управление потоком исполнения (исключительными ситуациями). Тут мы сталкиваемся с тем фактом, что каждая хоть сколь-нибудь сложная программа многогранна, и попытки сделать "красиво" с одной точки зрения тут же всё портят с других точек зрения. Против этого тоже есть приёмы.

P.S. Как любезно напомнил Dale, ещё нужна оптимальная стратегия. Она здесь уже добавлена в 3-х дополнительных строчках. Также добавлен случайный выбор, кому ходить первым.
Записан

Программировать - значит понимать (К. Нюгард)
Невывернутое лучше, чем вправленное (М. Аврелий)
Многие готовы скорее умереть, чем подумать (Б. Рассел)
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines