Текст
                    1


Валерий Рубанцев Исполнительный Робот, или Головоломки для программистов 2
Бесплатное издание Все права защищены. Никакая часть этой книги не может быть воспроизведена в любой форме без письменного разрешения правообладателей. Автор книги не несёт ответственности за возможный вред от использования информации, составляющей содержание книги и приложений. Copyright 2015 Валерий Рубанцев Лилия Рубанцева 3
От автора Нельзя научиться программировать, усердно штудируя учебник. Тут нужна практика! Только перерешав сотню-другую задач, вы твёрдо усвоите основы языка паскаль. В этой книге вы найдёте необходимый и достаточный материал для отработки навыков программирования по начальному курсу языка PascalABC.NET. При обучении очень важна наглядность. В предлагаемых заданиях она достигается за счёт использования Исполнителя, который называется Роботом. Это виртуальный автомат, умеющий выполнять определённый набор команд для перемещения по полю и закрашивания клеток. Все наборы заданий для Робота были созданы Станиславом Станиславовичем Михалковичем, руководителем проекта PascalABC.NET, в 2002-2007 годах. Они идеально подходят для изучения программирования по таким темам как: • • • • • • • • • • • • • переменные внутриблочные переменные оператор присваивания комбинированные операторы присваивания математические операции логические операции or и and логические выражения и условный оператор if-else цикл с параметром for цикл с условием while вложенные циклы процедуры без параметров процедуры с параметрами операторы exit и break Так как управлять Роботом невозможно без предварительной разработки алгоритма, то выполнение заданий хорошо развивает и укрепляет: • логическое и алгоритмическое мышление • умение находить и исправлять ошибки в алгоритме 4
• умение находить оптимальные решения Имея правильный алгоритм, вы легко сможете перевести его на любой процедурный язык: паскаль, Си-шарп, Дельфи, Яву или Питон. Поэтому решение задач на паскале в будущем существенно облегчит вам изучение других, не «учебных» языков программирования. В книге имеется исчерпывающая информация по каждому набору заданий, а также подробно разбирается решение всех задач (а их около полутора сотен!). Но это не значит, что вы должны просто копировать готовые решения! Сначала попробуйте решить задачу самостоятельно. Ваше решение не обязательно должно совпадать с решением автора книги. И только при затруднениях вы можете «подглядывать» в книгу. Решив задачу собственным способом, сравните его с предложенным. Может быть, ваше решение окажется лучше… Задания хотя и занимательны по форме, но вполне серьёзны по содержанию, так что вам нередко придётся хорошенько подумать, разрабатывая алгоритм. Однако наглядное воплощение алгоритма в виде перемещения Робота по полю сильно облегчит вам понимание сути заданий и отладку алгоритмов. Но имейте в виду, что система PascalABC.NET строго следит за правильностью выполнения заданий, поэтому не надейтесь решить задачу по принципу Вовки-вТридевятом-царстве: А, ладно, и так сойдёт! В конце книги вы найдёте полезные советы по решению заданий. Прежде чем приступать к работе, прочитайте их. Другие приложения пригодятся вам при изучении команд Робота, а также при составлении собственных заданий. Валерий Рубанцев 5
Условные обозначения, принятые в книге: Дополнение или замечание Ненавязчивое требование или указание Исходный код Задание для самостоятельного решения Проект Исходный код программы. Исходные коды всех проектов находятся в папке _Projects 6
Оглавление Исполнительный Робот , или .......................................... 2 Головоломки для программистов ..................................... 2 От авт ора .................................................................. 4 Оглавление ................................................................. 7 Исполнители ................................................................................................... 13 Исполнитель Робот .................................................................................................. 14 Дрессируем Робота .................................................................................................. 21 Задание а2 .................................................................................................................. 31 Задание а3 ..................................................................................................................34 Задание а4 ..................................................................................................................35 Набор заданий с.................................................... 37 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание с1 ...................................................................................................................38 с2 .................................................................................................................. 41 с3 ..................................................................................................................43 с4 ..................................................................................................................44 с5 ..................................................................................................................45 с6 ..................................................................................................................47 с7 ..................................................................................................................49 с8 .................................................................................................................. 51 с9 ..................................................................................................................53 с10 ................................................................................................................55 с11 .................................................................................................................56 с12 ................................................................................................................58 с13 ................................................................................................................60 с14 ................................................................................................................62 с15 ................................................................................................................64 с16 ................................................................................................................67 7
Набор заданий if ................................................... 69 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание if1 ................................................................................................................. 69 if2 ................................................................................................................ 73 if3 ................................................................................................................ 75 if4 ................................................................................................................ 78 if5 ................................................................................................................ 79 if6 ................................................................................................................. 81 if7 ................................................................................................................ 83 if8 ................................................................................................................ 84 if9 ................................................................................................................ 86 if10 ............................................................................................................... 89 if11 ................................................................................................................ 91 Набор заданий w ................................................... 94 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание w1.................................................................................................................. 94 w2 ................................................................................................................. 96 w3 ................................................................................................................. 98 w4 ................................................................................................................. 99 w5 ............................................................................................................... 100 w6 ................................................................................................................ 101 w7 ............................................................................................................... 103 w8 ............................................................................................................... 104 w9 ............................................................................................................... 106 w10 ............................................................................................................. 108 w11 ............................................................................................................... 110 w12 ...............................................................................................................111 w13 .............................................................................................................. 113 w14 .............................................................................................................. 114 w15 .............................................................................................................. 115 w16 .............................................................................................................. 117 w17 .............................................................................................................. 118 Набор заданий cif ................................................. 120 Задание cif1 ............................................................................................................. 120 Задание cif2 ............................................................................................................. 121 8
Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание cif3 ............................................................................................................. 123 cif4 ............................................................................................................. 124 cif5 ............................................................................................................. 125 cif6 ............................................................................................................. 126 cif7 ............................................................................................................. 127 cif8 ............................................................................................................. 129 cif9 ............................................................................................................. 130 cif10 ........................................................................................................... 132 cif11 ............................................................................................................ 133 cif12 ........................................................................................................... 135 cif13 ........................................................................................................... 136 cif14 ........................................................................................................... 137 cif15 ........................................................................................................... 139 cif16 ........................................................................................................... 140 cif17 ........................................................................................................... 143 cif18 ........................................................................................................... 145 cif19 ........................................................................................................... 146 cif20........................................................................................................... 148 cif21 ........................................................................................................... 150 cif22........................................................................................................... 152 Набор заданий count .............................................. 158 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание count1......................................................................................................... 158 count2 ........................................................................................................ 160 count3 ........................................................................................................ 162 count4 ........................................................................................................ 163 count5 ........................................................................................................ 165 count6 ........................................................................................................ 167 count7 ........................................................................................................ 169 count8 ........................................................................................................ 172 count9 ........................................................................................................ 173 count10 ...................................................................................................... 174 count11 ....................................................................................................... 175 count12 ...................................................................................................... 177 count13 ...................................................................................................... 178 9
Задание Задание Задание Задание count14...................................................................................................... 180 count15....................................................................................................... 181 count16...................................................................................................... 182 count17...................................................................................................... 184 Набор заданий cc.................................................. 187 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание cc1 .............................................................................................................. 187 cc2.............................................................................................................. 188 cc3.............................................................................................................. 190 cc4............................................................................................................... 191 cc5.............................................................................................................. 193 cc6.............................................................................................................. 194 cc7.............................................................................................................. 196 cc8.............................................................................................................. 197 cc9.............................................................................................................. 199 cc10 ............................................................................................................ 200 cc11 ............................................................................................................ 202 cc12 ............................................................................................................ 203 cc13 ............................................................................................................ 205 cc14 ............................................................................................................ 207 cc15 ............................................................................................................ 209 cc16 ............................................................................................................ 212 cc17 ............................................................................................................ 214 cc18 ............................................................................................................ 215 cc19 ............................................................................................................ 217 Набор заданий mix ................................................ 219 Задание Задание Задание Задание Задание Задание Задание Задание mix1 ............................................................................................................ 219 mix2 ........................................................................................................... 222 mix3 ........................................................................................................... 224 mix4 ........................................................................................................... 227 mix5 ........................................................................................................... 229 mix6 ........................................................................................................... 232 mix7 ........................................................................................................... 233 mix8 ........................................................................................................... 235 10
Задание mix9........................................................................................................... 237 Задание mix10 .......................................................................................................... 241 Набор заданий p .................................................. 247 Задание Задание Задание Задание Задание Задание Задание Задание Задание Задание p1 ................................................................................................................ 247 p2 ............................................................................................................... 249 p3 ................................................................................................................ 251 p4 ............................................................................................................... 252 p5 ............................................................................................................... 254 p6 ............................................................................................................... 256 p7 ............................................................................................................... 258 p8 ............................................................................................................... 260 p9 ............................................................................................................... 262 p10 ............................................................................................................. 264 Набор заданий pp ................................................. 267 Задание Задание Задание Задание Задание Задание Задание Задание pp1.............................................................................................................. 267 pp2 ............................................................................................................. 269 pp3 .............................................................................................................. 271 pp4 ............................................................................................................. 273 pp5 ............................................................................................................. 276 pp6 ............................................................................................................. 277 pp7 .............................................................................................................. 281 pp8 ............................................................................................................. 283 Набор заданий examen............................................ 286 Задание Задание Задание Задание Задание Задание Задание Задание Задание examen1 .................................................................................................... 286 examen2 ................................................................................................... 288 examen3 ................................................................................................... 290 examen4 ................................................................................................... 292 examen5 ................................................................................................... 294 examen6 ................................................................................................... 295 examen7 ................................................................................................... 297 examen8 ................................................................................................... 299 examen9 ................................................................................................... 300 11
Задание examen10 .................................................................................................. 303 Приложения ............................................................. 305 Наборы заданий ..................................................................................................... 305 Система команд Робота ........................................................................................ 306 Советы по решению заданий ............................................................................. 310 Разработка собственных наборов заданий ..................................................... 320 Набор RVRobot ........................................................................................................ 329 Задание rvrobot1 .................................................................................................... 329 Задание rvrobot2 ................................................................................................... 331 Задание rvrobot3 ................................................................................................... 332 Задание rvrobot4 ................................................................................................... 334 Литература ............................................................... 336 12
Исполнители Исполнители – это объекты программы, которые умеют выполнять определённый набор команд, который называют также системой команд исполнителя (СКИ). Исполнители традиционно используются для обучения детей программированию. Первым исполнителем была знаменитая Черепашка в языке программирования Лого (Рис. 1). Рис. 1. Знаменитая Черепашка-долгожительница Теперь Черепашки имеются во многих языках программирования, например, в Смолл Бейсике и Питоне. Черепашка умеет поворачиваться и ползти вперёд на заданное расстояние, прочерчивания за собой прямую линию. Со времени создания Черепашки появилось много новых исполнителей – Кузнечик, Чертёжник, Хамстер. А в этом номере журнала вы познакомитесь с Роботом, который поможет вам наглядно изучить основные конструкции языка паскаль. 13
Исполнитель Робот У лукоморья дуб зелёный; Златая цепь на дубе том: И днём и ночью кот учёный Всё ходит по цепи кругом; Идёт направо - песнь заводит, Налево - сказку говорит. А.С. Пушкин, Руслан и Людмила Исполнитель Робот имеет покладистый нрав и умеет выполнять простые команды. Его рабочее место – игровое поле - представляет собой прямоугольник, разбитый на квадратные клетки. Сам Робот – это тоже квадрат, но, в отличие от белых клеток поля, он окрашен в жёлтый цвет и немного меньше клеток по размеру. В начале решения задачи он находится в стартовой позиции. Маленький квадратик в левом верхнем углу одной из клеток поля отмечает его финишную позицию. В ней Робот должен закончить свой путь. А его работа заключается в том, чтобы закрасить клетки поля, обозначенные точками, которые находятся в их центре. Игровое поле по периметру обнесено неприступной стеной, через которую Робот перешагнуть не может. Подобные стены могут разделять также клетки поля, и тогда Робот не сможет перейти из одной клетки в другую, даже в соседнюю (Рис. 1). Робот может переходить из одной клетки в другую, соседнюю, выполняя следующие команды (Рис. 2): Right – идти вправо Left – идти влево Down – идти вниз Up – идти вверх 14
Рис. 1. Робот и окружающая среда Давайте посмотрим, как выглядит исходная позиция задания а1 в запущенной программе (Рис. 3). Во второй строке вы видите Робота в исходной позиции. В первой, верхней строке располагаются 4 клетки с точками – их нужно закрасить. Левая верхняя клетка помечена маленьким квадратиком – в неё должен прийти Робот, когда закрасит все клетки. Сейчас Робот может сделать ход вниз, влево или вправо. Но если он попытается пойти вверх, то вы получите сообщение об аварии (Рис. 4), и выполнение задания прекратится. 15
Рис. 2. Направления переходов Рис. 3. «Вот и первое заданье…» 16
Рис. 4. ДТП Итак, у Робота остаётся для начала только 3 хода на выбор: пойти налево, направо или вниз. Любой из этих ходов допустим, но Робот должен не просто бродить по полю, а стремиться как можно быстрее выполнить задание. Это значит, что нужно за минимальное число ходов добраться до первой клетки с точкой и закрасить её. На Рис. 3⬆ хорошо видно, что для Робота первая клетка с точкой – это самая правая из них. Робот не сможет добраться до других точек, минуя её. Легко догадаться, что, прежде всего, Робот должен выполнить 4 шага вправо, чтобы обогнуть стену (Рис. 5). Теперь он может беспрепятственно подняться в верхнюю горизонталь (Рис. 6). И тут начинаются настоящие малярные работы! Запомните: Робот может закрасить только ту клетку, в которой он находится. 17
Значит, он должен сделать шаг влево, а затем уже закрасить клетку (Рис. 7). Рис. 5. Идём направо Рис. 6. И вверх 18
Рис. 7. Первая закраска! Эти действия он должен повторить трижды, чтобы закрасить и остальные клетки (Рис. 8). 19
Рис. 8. Все клетки закрашены И Роботу осталось сделать последний, решающий шаг влево, чтобы закончить задание (Рис. 9). Рис. 9. Задание выполнено! 20
Дрессируем Робота Итак, на бумаге мы справились с задачей. Действительно, Робот только выполнял наши команды, поэтому слава и почёт должны достаться авторам, а не исполнителям. Но как заставить Робота, даже самого послушного выполнять наши команды в реальной программе? – А их нужно записать в исходном коде, который Робот умеет читать. Но сначала мы должны научиться получать задания для последующего их осмысливания и исполнения. Самый простой и в то же время правильный путь в этом направлении – нажать кнопку Создать шаблон программы на Панели инструментов (Рис. 1) или клавиши Ctrl+Shitf+L. Рис. 1. Шаблонная кнопка Откроется диалоговое окно Загрузка задания (Рис. 2). Рис. 2. Вступаем в диалог 21
Внизу вы можете прочитать, какие имеются группы заданий. Нам нужны задания, которые начинаются с букв RB, которые означают Robot. Набираем в текстовом поле Задание эти буквы (в любом регистре) и получаем внизу диалогового окна буквы для наборов заданий (Рис. 3). Рис. 3. Префиксы и суффиксы Нам нужен самый первый набор а, который предназначен для предварительного знакомства с системой команд Робота. Добавляем в текстовое поле Задание букву а и опять получаем подсказку: в наборе 4 задания (Рис. 4). Рис. 4. Грамотно пользуйтесь подсказками 22
Конечно, начинать нужно с первого задания, поэтому печатаем цифру 1 (Рис. 5). Рис. 5. Можно начинать По умолчанию все ваши проекты-решения будут сохраняться в папке PascalABC.NET Projects, название которой напечатано в текстовом поле Каталог. Если вы хотите изменить место хранения своих проектов, то нажмите кнопку с жёлтой папкой и укажите новый путь (Рис. 6). Рис. 6. Пути выбирают Все приготовления закончены, и мы нажимаем кнопку Загрузка или клавишу ВВОД (Рис. 7). В Редакторе кода появилась страница RBa1.pas с заготовкой кода для задания а1 (Рис. 8). 23
Если у вас на то есть охота, то вы можете и самостоятельно набрать весь этот код и сохранить его на диске под нужным именем. Рис. 7. Всё готово Рис. 8. Заготовительно-приготовительные работы закончены Чтобы не выбирать каждый раз папку для хранения файлов, сохраните задание на диске, закройте среду разработки, перейдите в папку с файлом и дважды щёлкните по нему. Заготовка задания откроется в Редакторе кода, а при загрузке нового задания в текстовом поле Каталог будет автоматически напечатан путь к нужной папке. Строчка uses Robot; 24
подключает к проекту модуль Robot.pas, без которого Робот не появится. В главной части программы записан вызов процедуры Task, которой мы передаём строку 'a1' с названием задания. Вы можете запустить программу, нажав кнопку Выполнить на Панели инструментов или (что лучше) воспользоваться командой меню Программа > Выполнить без связи с оболочкой. На экране появится окно с заданием, игровое поле и элементы управления (Рис. 9). Рис. 9. Конкретное задание Всегда внимательно читайте верхнюю строку, в которой находится задание для Робота. 25
В нижней части окна выводятся сообщения о текущем состоянии программы. Сейчас вы можете прочитать там, что Робот готов к выполнению команд. Кнопка Выход (клавиша Esc) закрывает программу. Кнопка Справка (клавиша F1) открывает одноимённое диалоговое окно, в котором перечислены все команды Робота, а также условия, которые может проверять Робот по ходу выполнения задания (Рис. 10). Рис. 10. Всегда держите под рукой! Команды перемещения Робота вам уже известны, а новая команда Paint закрашивает клетку, в которой Робот находится. 26
При отладке команд следует пользоваться пошаговым перемещением Робота, чтобы внимательно наблюдать за каждым его действием. При обнаружении ошибки, нужно исправить исходный код и снова запустить программу. Чтобы выполнить 1 шаг, нажмите кнопку Шаг или клавишу ПРОБЕЛ (Рис. 11). Рис. 11. Пошагали? Набравшись смелости, мы дерзко нажимаем на эту кнопку, но Робот не двигается с места, а мы получаем обидное сообщение, что Работа окончена, задание не выполнено (Рис. 12). Всё верно, ведь мы не дали Роботу ни одной команды! 27
Рис. 12. Работа стоит! Возвращаемся в Редактор кода и после вызова процедуры Task аккуратно выписываем команды, которые придумали раньше: uses Robot; begin Task('a1'); //идём направо без закраски: Right; Right; Right; 28
Right; //поднимаемся вверх без закраски: Up; //идём налево с закраской: Left; Paint; Left; Paint; Left; Paint; Left; Paint; //идём налево без закраски: Left; end. Если вы внимательно читали журнальчик, то здесь для вас нет ничего нового, поэтому запустите программу и нажимайте кнопку Шаг. Робот будет послушно переходить из одной клетки в другую, выполняя команды, пока, наконец, не окажется на базе. Следующее нажатие на кнопку Шаг остановит программу, которая порадует вас приятным сообщением, что Задание выполнено (Рис. 13). Закройте программу, а затем снова запустите её. На этот раз нажмите кнопку Пуск, и Робот самостоятельно и быстро выкрасит все заданные клетки и окажется на базе. Программа также сообщит нам, что Роботу потребовалось сделать 15 шагов для выполнения задания. Если вы пересчитаете команды, то их окажется только 14. Лишняя команда появилась из-за того, что при запуске программы считается, что Робот уже сделал 1 шаг, хотя он и не сдвинулся с места. Скорость перемещения Робота можно изменять ползунком Скорость. Двигайте его вправо, что Робот шёл быстрее, влево – медленнее. Сохраните исходный код программы на диске и готовьтесь к новым испытаниям! 29
Рис. 13. С выполнением! 30
Задание а2 Получить заготовку для второго задания ещё проще, чем для первого! Нажмите кнопку Создать шаблон программы и исправьте единицу на двойку (Рис. 1). Рис. 1. Идём дальше Если вы сохраняете решения не в папке по умолчанию, то укажите её, как мы это делали раньше, а затем нажмите кнопку Загрузка. В Редакторе кода откроется заготовка (Рис. 2). Так как прежде чем браться за выполнение задания, его нужно увидеть, то запустите программу и внимательно посмотрите на картинку с заданием (Рис. 3). 31
Рис. 2. Новая заготовка Рис. 3. Требуется особое внимание Если вы не можете сразу найти решение, то скопируйте игровое поле на листочек в клеточку и нарисуйте путь Робота. В данном случае понятно, что самый короткий путь для Робота такой: • обойти клетки с точка по часовой стрелке или 32
• обойти клетки с точками против часовой стрелки Что касается начальной клетки, то её можно закрасить сразу, а можно и в конце выполнения задания, поскольку Робот всё равно в неё должен вернуться. Я выбрал вариант движения по часовой стрелке без закраски начальной клетки, тогда Робот сначала идёт вправо, закрашивает клетку, опять идёт вправо и закрашивает клетку. И эти действия он повторяет по остальным направлениям – вниз, влево, вверх. Вернувшись на базу, он закрашивает последнюю клетку – и задача решена! Переписать команды в главную часть программы не составляет никакого труда: Task('a2'); //идём направо: Right;Paint; Right;Paint; //идём вниз: Down;Paint; Down;Paint; //идём налево: Left;Paint; Left;Paint; //идём вверх: Up;Paint; Up;Paint; 33
Задание а3 Получите заготовку для третьего задания, запустите программу и ознакомьтесь с игровой ситуацией (Рис. 1). Рис. 1. Третье задание Тут, как говорится, без вариантов. Робот обязательно должен закрасить начальную клетку, потому что он в неё больше не вернётся. А дальше он двигается зигзагом: вниз – вправо – вверх – вправо, и так до базовой клетки. Каждую клетку Робот должен закрасить. Задание очень простое: Task('a3'); //закрашиваем первую клетку: Paint; //идём вниз и закрашиваем вторую клетку: Down; Paint; //идём вправо: Right; Paint; 34
//идём вверх: Up; Paint; //идём вправо: Right; Paint; //идём вниз: Down; Paint; //идём вправо: Right; Paint; //идём вверх: Up; Paint; Задание а4 И вот мы дошли до последнего задания в первом наборе (Рис. 1). Рис. 1. Четвёртое задание Задача чуть сложнее предыдущей, но тоже решается в уме. 35
Идём вверх до упора, переходим вправо, опускаемся до дна, закрашиваем там клетку, выбираемся наверх, и так далее: Task('a4'); //поднимаемся до верхней горизонтали: Up;Up; //идём вправо до второй колонки: Right; //опускаемся до дна и закрашиваем клетку: Down;Down;Paint; //поднимаемся до верхней горизонтали: Up;Up; //идём вправо до третьей колонки: Right; //опускаемся до дна и закрашиваем клетку: Down;Down;Paint; //поднимаемся до верхней горизонтали: Up;Up; //идём вправо до четвёртой колонки: Right; //опускаемся до дна и закрашиваем клетку: Down;Down;Paint; //задание выполнено //возвращаемся на базу: Up;Up; Left;Left;Left; Down; Down; 36
Набор заданий с Откройте диалоговое окно Загрузка задания, наберите в текстовом поле Задание волшебные буквы RBc – и вы увидите, что вам предстоит решить ни много ни мало 16 заданий на цикл с параметром (Рис. 1). Рис. 1. 16 циклов! Вы, должно быть, решая первые задания, уже заметили, что одни и те же команды повторяются несколько раз. В исходном коде такие действия описываются совершенно одинаково. Чтобы уменьшить «писанину» и сделать исходный текст более наглядным, повторяющиеся куски кода сокращают за счёт циклов. Если мы заведомо знаем, сколько раз повторяются действия, то вполне разумно выбрать простой цикл for. Вернитесь в диалоговое окно, допишите единицу и нажмите кнопку Загрузка (Рис. 2). Рис. 2. Перезагрузка 37
Задание с1 Заготовка кода для второго набора заданий отличается от первого только названием самого задания (Рис. 1). Рис. 1. По шаблону становись! Вся прелесть – в игровом поле (Рис. 2). Рис. 2. Становится интереснее! 38
Ни одной клетки в первом задании закрашивать не нужно – достаточно только кратчайшим путём добраться до базы. Сворачивать некуда, поэтому идём строго вправо. Давайте посчитаем на пальцах, сколько шагов должен сделать Робот в этом направлении: один – два – три - . . . – десять. Итак, Робот должен 10 раз выполнить команду Right. Это значит, что переменная цикла for должна последовательно принимать значения от 1 до 10: uses Robot; begin Task('c1'); //идём вправо 10 шагов: for var i := 1 to 10 do Right; end. Проверим теорию практикой. Всё верно: Робот благополучно добрался до базы и с честью выполнил задание (Рис. 3)! Вы можете заменить цикл for циклом foreach, а значения переменной цикла задавать с помощью функции Range, которая с указанными выше значениями параметров вернёт последовательность целых чисел в диапазоне 1..10: foreach var i in Range(1, 10) do Right; Можно обойтись и без функции Range: foreach var i in [1..10] do Right; Результат вы, конечно, получите тот же самый, что и раньше, но тренировка с циклом foreach тоже лишней не будет! 39
Рис. 3. Ушёл на базу 40
Задание с2 Переходим к следующему заданию (Рис. 1). Рис. 1. Конца нет! Оно очень похоже на первое задание, но теперь Робот по ходу движения должен ещё и закрашивать клетки. Обратите внимание, что базовая клетка должна остаться чистой! Ищем повторяющиеся команды. Десять раз мы должны закрасить клетку и десять раз перейти в правую клетку. Таким образом, «период» цикла такой: Paint – Right (Рис. 2). 41
Рис. 2. Первый период Всё остальное – дело программистской техники, то есть компьютера: //10 раз закрашиваем клетку и //идём направо: for var i := 1 to 10 do begin Paint; Right; end; 42
Задание с3 Не правда ли, третье задание – почти второе, только крайние клетки поменялись местами по части закрашивания (Рис. 1). Рис. 1. Чувствуете разницу? «Период» цикла напрашивается сам собой: сначала идём вправо, затем закрашиваем клетку. И так 10 раз кряду: //10 раз переходим вправо и //закрашиваем клетку: for var i := 1 to 10 do begin Right; Paint; end; 43
Задание с4 Здесь мы видим обобщение двух предыдущих задач: все клетки по ходу движения вправо нужно закрасить (Рис. 1). Рис. 1. Красят все! Мы можем действовать, как в Задании с2, но тогда у нас останется незакрашенной базовая клетка, и нам придётся приложить дополнительные усилия, чтобы закончить задание: //10 раз закрашиваем клетку и //переходим вправо: for var i := 1 to 10 do 44
begin Paint; Right; end; //закрашиваем базовую клетку: Paint; Задание с5 Легко и здесь увидеть Задание с2, но теперь Робот должен перемещаться не только вправо, но и вниз – по диагонали (Рис. 1). Рис. 1. Наклонная дорожка 45
Если Робот будет переходить только вправо, то закрасит верхнюю горизонталь. Значит, после каждого хода вправо он должен опуститься вниз на 1 клетку, чтобы перейти на диагональ (Рис. 2). Рис. 2. Можно изобразить и другое колено //10 раз закрашиваем клетку, //идём направо и вниз: for var i := 1 to 10 do begin Paint; Right; Down; end; 46
Задание с6 А вот эта задача похитрей! Тут нужно закрашивать клетки через одну, двигаясь влево, а затем победным маршем вернуться на базу (Рис. 1). Рис. 1. Полевело Таким образом, налицо 2 два цикла: • двигаемся влево и закрашиваем клетки через одну • двигаемся вправо без закрашивания клеток Второй цикл слишком прост, чтобы тратить время на его обсуждение. 47
Чтобы найти «период» первого цикла, выполним команды: Left – Paint – Left. Тогда мы закрасим самую правую клетку с точкой, а Робот окажется в той же ситуации, что и в начале движения, но уже перед второй закрашиваемой клеткой. Всего он должен закрасить 8 клеток, значит, и цикл мы должны повторить 8 раз (Рис. 2). Рис. 2. Несложный цикл //8 раз переходим влево, //закрашиваем клетку и //снова переходим влево: for var i := 1 to 8 do begin Left; Paint; Left; end; //возвращаемся на базу: for var i := 1 to 16 do Right; 48
Задание с7 В этом задании мы вновь встречаемся с зигзагом, но с весьма замысловатым (Рис. 1). Рис. 1. Зигзаг удачи? Как всегда, в циклах важно ухватить минимальное число повторяющихся команд. Давайте начнём выполнять задание: Paint – Down - Paint – Left - Paint – Up – Paint – Left 49
Пока никаких повторяющихся действий мы не наблюдаем. Продолжаем: Paint – Down - Paint – Left… Ура! Дальше всё то же самое. Считаем, сколько раз. – Пять, и задача решена (Рис. 2). Рис. 2. Длинный период //5 раз выполняем зигзаг: for var i := 1 to 5 do begin Paint; Down; Paint; Left; Paint; Up; Paint; Left; end; Если присмотреться к условию задачи внимательнее, то вполне можно увидеть в ней усложнённый вариант Задания с2. 50
Задание с8 Здесь мы видим «гребёнку», подобную которой уже решали (Рис. 1). Рис. 1. На гребне Так как последнюю клетку также нужно закрасить, то нам потребуется ещё одна, дополнительная команда Paint. А «период» цикла легко найти, начав решать задачу: Paint – Down – Left – Up. За 10 итераций мы закрасим 10 клеток и попадём на базовую (Рис. 2). 51
Рис. 2. Слегка криво Её нужно закрасить – и только: дальше команды «периода» не выполняются: //10 раз закрашиваем клетку, //а затем переходим: //вниз - налево - вверх: for var i := 1 to 10 do begin Paint; Down; Left; Up; end; //закрашиваем последнюю клетку: Paint; 52
Задание с9 В этом задании мы видим настоящий «слалом» (Рис. 1). Рис. 1. Слалом-зигзаг Но если приглядеться, то мы опять найдём вариант Задания с2, поэтому период цикла выписать очень легко: Paint – Up – Right – Paint – Down- Right. Теперь Робот окажется в той же позиции, что и в начале движения, но на 2 клетки правее (Рис. 2). 53
Рис. 2. Виртуозно! Всего Роботу предстоит совершить 5 переходов: for var i := 1 to 5 do begin Paint; Up; Right; Paint; Down; Right; end; 54
Задание с10 А в этом задании мы найдём и Задание с2, и Задание с3 - но в обратном направлении (Рис. 1). Рис. 1. Слоёный путь Первую часть задачи решаем, как в Задании с2, затем переходим на нижнюю горизонталь и выполняем действия Задания с3, но влево: //идём вправо по верхней горизонтали: for var i := 1 to 10 do begin Paint; Right; 55
end; //спускаемся на нижнюю горизонталь: Down; //идём влево по нижней горизонтали: for var i := 1 to 10 do begin Left; Paint; end; Задание с11 В Задании с11 мы также без труда найдём пару Заданий с4 (Рис. 1). Рис. 1. На 2 точки больше 56
Таким образом, сначала мы идём влево, как в Задании с4, затем переходим на нижнюю горизонталь и идём в противоположном направлении: //идём влево по верхней горизонтали: for var i := 1 to 9 do begin Paint; Left; end; Paint; //опускаемся на нижнюю горизонталь: Down; //идём вправо по нижней горизонтали: for var i := 1 to 9 do begin Paint; Right; end; Paint; 57
Задание с12 Здесь мы снова найдём Задание с4, но из двух частей (Рис. 1). Рис. 1. Туда-сюда-обратно Сначала мы идём влево и закрашиваем по пути клетки, затем идём вправо до первой незакрашенной клетки. Повторяем действия Задания с4 для оставшихся справа незакрашенных клеток и, наконец, возвращаемся на базу: //делаем 5 шагов влево //и закрашиваем клетки: for var i := 1 to 5 do begin Paint; Left; 58
end; Paint; //возвращаемся в исходную позицию + 1: for var i := 1 to 6 do begin Right; end; //делаем 5 шагов вправо //и закрашиваем клетки: for var i := 1 to 4 do begin Paint; Right; end; Paint; //возвращаемся на базу: for var i := 1 to 5 do begin Left; end; Попробуйте решить эту задачу другим способом! 59
Задание с13 А тут мы видим четыре Задания с2, которые «закольцованы» (Рис. 1). Рис. 1. Ходим в квадрате по кругу Квадрат можно обходить или против часовой стрелки, или по ходу её движения: //идём вниз: for var i := 1 to 8 do begin Paint; Down; end; 60
//идём налево: for var i := 1 to 8 do begin Paint; Left; end; //идём вверх: for var i := 1 to 8 do begin Paint; Up; end; //идём вправо: for var i := 1 to 8 do begin Paint; Right; end; 61
Задание с14 Тот же самый квадрат, но повёрнутый ромбиком (Рис. 1). Рис. 1. Квадратный ромб //идём вверх вправо: for var i := 1 to 4 do begin Paint; Up; Right; end; //идём вниз вправо: 62
for var i := 1 to 4 do begin Paint; Down; Right; end; //идём вниз влево: for var i := 1 to 4 do begin Paint; Down; Left; end; //идём вверх влево: for var i := 1 to 4 do begin Paint; Up; Left; end; 63
Задание с15 Пара Заданий с12, расположенных крестиком (Рис. 1). Рис. 1. Крестовый поход Робот может начать свой поход в любую сторону. Например, можно сначала закрасить левую ветвь креста: //закрашиваем клетки и //идём налево: for var i := 1 to 5 do begin Paint; Left; 64
end; Paint; Вернуться на первую незакрашенную клетку в горизонтальной части креста: //возвращаемся в исходную позицию + 1: for var i := 1 to 6 do begin Right; end; Закрасить правую горизонтальную ветвь: //закрашиваем клетки и //идём направо: for var i := 1 to 4 do begin Paint; Right; end; Paint; Снова вернуться в исходную клетку: //возвращаемся в исходную позицию: for var i := 1 to 5 do begin Left; end; Аналогично закрасить клетки вертикальной части креста: //идём вверх и //закрашиваем клетки: for var i := 1 to 5 do begin 65
Up; Paint; end; //возвращаемся в исходную позицию + 1: for var i := 1 to 6 do begin Down; end; //закрашиваем клетки и //идём вниз: for var i := 1 to 4 do begin Paint; Down; end; Paint; //возвращаемся на базу: for var i := 1 to 5 do begin Up; end; 66
Задание с16 Похоже на Задание с8, но гораздо «хитрее» (Рис. 1). Рис. 1. Хитро загнуто! Здесь две «гребёнки» - нижняя и верхняя, причём «зубья» идут через один. Это значит, что сначала Робот должен пройти нижнюю гребёнку вправо, подняться верхнюю гребёнку и пройти её в противоположном направлении. Мы без труда подсчитаем, что Робот должен закрасить по 5 клеток в каждой из гребёнок. Найти «период» цикла также не составляет труда: 67
//нижний зигзаг: for var i := 1 to 5 do begin Paint; Down; Right; Right; Up; end; //переход на верхний зигзаг: Up; Left; Down; //верхний зигзаг: for var i := 1 to 5 do begin Paint; Up; Left; Left; Down; end; //возвращаемся на базу: Down; Right; Up; 68
Набор заданий if Если в первых двух наборах заданий игровое поле однозначно определяло поведение Робота на поле, то в наборе заданий if поле может быть нескольких видов, которые возникают на экране случайно, и мы не можем предугадать, какое именно поле достанется Роботу. Это значит, что Робот должен на месте самостоятельно определить вид поля и соответственно этому выбрать нужную последовательность команд. Для этого он использует условный оператор if – else. Чтобы перейти к новому набору заданий, откройте диалоговое окно Загрузка задания и наберите в текстовом поле Задание: RBif1 (Рис. 1). Рис. 1. Новый наборчик Как вы видите, в этом наборе 11 заданий, и их выполнение предполагает использование в исходном коде условного оператора выбора if – else. Задание if1 Так как нам заранее неизвестно, сколько разных полей может быть в задании, то придётся несколько раз запустить программу, чтобы их «вычислить». Таким нехитрым способом можно выяснить, что первое задание в этом наборе содержит 2 вида полей (Рис. 1). 69
Рис. 1. Их становится больше! В первую очередь, Робот должен определить вид поля, на котором он находится. Для этого у него на голове имеется радар, который умеет находить стены по всем четырём направлениям от клетки с Роботом. К сожалению, радар очень слабый, поэтому может «видеть» только стены по границам клетки с Роботом. В программе информацию от радара мы можем получать от следующих функций: WallFromLeft – возвращает True, если слева от Робота стена WallFromRight – возвращает True, если справа от Робота стена WallFromUp – возвращает True, если сверху от Робота стена WallFromDown – возвращает True, если снизу от Робота стена Теперь давайте проанализируем информацию, представленную на Рис. 1⬆. Вокруг клетки с Роботом нет ни одной стены, поэтому он пока не может определить вид игрового поля. Можно пойти вниз или вверх, тогда в обоих случаях у Робота появится стена справа, что также не даст ему возможности определиться на местности. Остаётся шаг вправо. И в этом случае у Робота обязательно появится стена справа, но в первом случае (на Рис. 2, слева) вторая стена будет располагаться снизу, а во втором случае (на Рис. 2, справа) – сверху. 70
Рис. 2. Считаем стены Таким образом, сделав шаг вправо, Робот сможет определить вид поля: //поле может быть двух видов! begin Task('if1'); Right; Если стена окажется ниже, то Робот должен выполнить команды: Up - Right Down – Paint. Если стена окажется выше, то Робот должен выполнить команды: Down - Right - Up – Paint. Оставшуюся часть программы уже нетрудно написать: //стена снизу: if (WallFromDown) then begin Up; Right; Down; Paint; 71
end //стена сверху: else begin Down; Right; Up; Paint; end; Теперь нашего доблестного Робота никакое поле не поставит в тупик (Рис. 3)! Рис. 3. Выход всегда есть! 72
Задание if2 Во втором задании необходимо закрасить клетки у соседних стен (Рис. 1). Рис. 1. Будем красить Стен может быть: 0..4. Если стены есть, но их меньше четырёх, то они могут располагаться по-разному относительно клетки с Роботом. Вариантов получается слишком много, чтобы расписывать их каждый в отдельности. Нужно искать универсальный алгоритм! Прежде всего, закрасим клетку, в которой стоит Робот: //0..4 стен begin Task('if2'); //закрашиваем начальную клетку: Paint; Затем идём вверх и смотрим, есть ли стена в этом направлении. Если есть, то закрашиваем клетку перед ней, а затем спускаемся в стартовую клетку: //шаг вверх: Up; //если сверху стена if (WallFromUp) then //то 73
Paint; //закрашиваем клетку //возвращаемся в исходную позицию: Down; Дальше действуем аналогично во всех остальных направлениях: //шаг вниз: Down; if (WallFromDown) then Paint; Up; //шаг влево: Left; if (WallFromLeft) then Paint; Right; //шаг вправо: Right; if (WallFromRight) then Paint; Left; Независимо от числа стен и их расположения Робот всегда закрасит клетки, которые находятся рядом с ними (Рис. 2)! Рис. 2. Зелёный – любимый цвет роботов 74
Задание if3 Поле очень маленькое – всего 2 х 2 клетки, Робот находится в одном из углов поля, а закрасить должен клетку в противоположном углу (Рис. 1). Рис. 1. Противоположности сходятся В первую очередь, Робот должен определиться, в каком углу поля он находится. Сделать это нетрудно, если внимательно посмотреть на все 4 стороны. Если стена сверху и слева, то Робот находится в верхнем левом углу поля, а идти в противоположную клетку от должен так: Right – Down или Down – Right. 75
Чтобы не выписывать сложные логические условия, можно обозначить направление на стены степенями двойки, тогда по сумме направлений мы легко найдём положение Робота на поле: //направление на стены: var dir := 0; if (WallFromUp) then dir += 1; if (WallFromDown) then dir += 2; if (WallFromLeft) then dir += 4; if (WallFromRight) then dir += 8; Зная начальную клетку, мы перегоняем Робота в противоположный угол: if (dir = 5) then //верхний левый угол begin Down; Right; end else if (dir = 9) then //верхний правый угол begin Down; Left; end else if (dir = 6) then //нижний левый угол begin Up; Right; end else begin //нижний правый угол Up; Left; end; И закрашиваем единственную клетку: //закрашиваем клетку: Paint; 76
Алгоритм простой, но действует безотказно (Рис. 2)! Рис. 2. Всё гениальное – просто! 77
Задание if4 Очень лёгкое задание (Рис. 1)! Рис. 1. Полегчало… Робот определяет, в каком направлении находится стена, делает шаг в противоположном направлении и закрашивает клетку: //стена сверху: if (WallFromUp) then begin Down; Paint; end //стена снизу: else if (WallFromDown) then 78
begin Up; Paint; end //стена слева: else if (WallFromLeft) then begin Right; Paint; end //стена справа: else begin Left; Paint; end Задание if5 Это задание развивает идею предыдущего (Рис. 1). Рис. 1. Развивайте идеи! Стена может находиться в одном из четырёх направлений. Легко заметить, что для обеих вертикальных и для обеих горизонтальных стен нужно выполнить одни и те же действия, поэтому в условном операторе if нужно записать сложное логическое выражение с оператором or. 79
А действия Робота совершенно бесхитростные. Если, к примеру, стена горизонтальная, то он делает шаг налево и закрашивает клетку. Затем делает 2 шага вправо и закрашивает вторую клетку. Ещё 1 шаг влево – и Робот на базе. Можно сначала закрасить правую клетку, а затем левую. //стена слева или справа: if (WallFromLeft or WallFromRight) then begin //закрашиваем верхнюю клетку: Up; Paint; //закрашиваем нижнюю клетку: Down; Down; Paint; //возвращаемся на базу: Up; end else //стена сверху или снизу: begin //закрашиваем левую клетку: Left; Paint; //закрашиваем правую клетку: Right; Right; Paint; //возвращаемся на базу: Left; end; 80
Задание if6 Это задание отличается от предыдущих только тем, что место стены заняла закрашенная клетка (Рис. 1). Рис. 1. Анализируй всё! На этот случай Робот имеет «цветоанализатор», который через функции CellIsPainted – возвращает True если клетка, в которой находится Робот, закрашена CellIsFree – возвращает True если клетка, в которой находится Робот, не закрашена сообщает нам, в какой клетке находится Робот – в закрашенной или в чистой. 81
Робот последовательно делает шаги во всех четырёх направлениях. Если он попадает на закрашенную клетку, то делает 2 шага в противоположном направлении и закрашивает клетку. Если он не обнаруживает закрашенной клетки, то возвращается в исходную позицию, чтобы выполнить шаг в следующем направлении: //шаг наверх: Up; //клетка закрашена? if (CellIsPainted) then begin Down; Down; Paint; end else //возвращаемся на базу Down; //шаг вниз: Down; //клетка закрашена? if (CellIsPainted) then begin Up; Up; Paint; end else //возвращаемся на базу Up; //шаг влево: Left; //клетка закрашена? if (CellIsPainted) then begin Right; Right; Paint; end else //возвращаемся на базу Right; //шаг вправо: Right; //клетка закрашена? if (CellIsPainted) then begin Left; Left; Paint; end 82
Задание if7 Полностью повторяет Задание if3 (Рис. 1). Рис. 1. Повторение – мать учения //направление на стены: var dir := 0; if (WallFromUp) then dir += 1; if (WallFromDown) then dir += 2; if (WallFromLeft) then dir += 4; if (WallFromRight) then dir += 8; 83
if (dir = 5) then //верхний левый угол begin Down; Right; end else if (dir = 9) then //верхний правый угол begin Down; Left; end else if (dir = 6) then //нижний левый угол begin Up; Right; end else begin //нижний правый угол Up; Left; end; //закрашиваем клетку: Paint; Задание if8 Робот находится между двух стен, которые могут быть вертикальными или горизонтальными (Рис. 1). Рис. 1. Между двух стен 84
Задание точно такое же, как if4, только закрасить нужно пару клеток: //стена сверху: if (WallFromUp) then begin Down; Paint; Down; Paint; end //стена снизу: else if (WallFromDown) then begin Up; Paint; Up; Paint; end //стена слева: else if (WallFromLeft) then begin Right; Paint; Right; Paint; end //стена справа: else begin Left; Paint; Left; Paint; end 85
Задание if9 Мудрёное задание со стенами (Рис. 1). Рис. 1. У стены Если стен меньше двух, то нужно закрасить исходную клетку (Рис. 1⬆ и 2). Если же на поле 2 стены, то следует закрасить 2 клетки – одну выше исходной, а другую – ниже (Рис. 3). 86
Рис. 2. Где стены? Рис. 3. Крутите головой! 87
//число стен: var nWall := 0; //идём влево: Left; //есть стена? if (WallFromDown then nWall += //возвращаемся: Right; //идём вправо: Right; //есть стена? if (WallFromDown then nWall += //возвращаемся: Left; or WallFromUp) 1; or WallFromUp) 1; //сколько насчитали стен: if (nWall < 2) then begin Paint; end else //2 стены begin //закрашиваем клетку выше: Up; Paint; //закрашиваем клетку ниже: Down; Down; Paint; //возвращаемся на базу: Up; end; 88
Задание if10 Вариации на тему «есть стены – нет стен», но с закрашенными клетками (Рис. 1). Рис. 1. В зелени Если закрашенных клеток слева и/или справа нет, то Робот должен закрасить стартовую клетку, в противном случае – клеткой выше (Рис. 2). Робот должен посчитать закрашенные клетки справа и слева от стартовой клетки. Если ни одной закрашенной клетки он не обнаружит, то закрашивает стартовую клетку. Если обнаружит, делает шаг вверх, закрашивает клетку и возвращается на базу 89
Рис. 2. Выбирай – но осторожно //число клеток: var nCell := 0; //идём влево: Left; //клетка закрашена? if (CellIsPainted) then nCell += 1; //возвращаемся: Right; //идём вправо: Right; //клетка закрашена? if (CellIsPainted) then nCell += 1; //возвращаемся: Left; //сколько насчитали закрашенных клеток: if (nCell = 0) then begin 90
Paint; end else // > 0 begin //закрашиваем клетку выше: Up; Paint; //возвращаемся на базу: Down; end; Задание if11 Последнее задание в этом наборе (Рис. 1). Рис. 1. Это конец! 91
Справа от Робота может быть 0..2 закрашенные клетки (Рис. 2). Он должен найти первую незакрашенную клетку в вертикали с этими клетками, которая находится не выше стартовой, закрасить её и проследовать на базу. Рис. 2. Могут быть варианты Первый ход Робота очевиден – он должен сделать шаг вправо: //шаг вправо: Right; Теперь Робот должен сосчитать закрашенные клетки и сохранить их число в переменной nCell, чтобы потом попасть на базу: //число закрашенных клеток: var nCell := 0; Если Робот сразу оказался на пустой клетке, то вниз он не пойдёт. Если же под ним закрашенная клетка, то он считает её и шагает вниз: 92
//шагать вниз, пока Робот //стоит на закрашенной клетке: if (CellIsPainted) then begin nCell += 1; Down; end; Затем он повторяет это действие ещё раз: if (CellIsPainted) then begin nCell += 1; Down; end; Независимо от числа закрашенных клеток сейчас Робот стоит на первой незакрашенной в текущей вертикали и закрашивает её: //Робот закрашивает первую //незакрашенную клетку в текущей //вертикали: Paint; Основная часть задания выполнена, Робот переходит на следующую вертикаль и поднимается на nCell клеток: //шаг вправо: Right; //и nCell шагов вверх до базы: for var i := 1 to nCell do Up; Робот на базе! 93
Набор заданий w Этот набор нацелен на отработку навыков применения цикла с условием while. В наборе 17 заданий, которые загружаются, как обычно (Рис. 1). Рис. 1. Хорошее число! Задание w1 В первом задании Робот должен идти вправо до базы, причём длина пути заранее неизвестна, поэтому мы не можем использовать цикл for (Рис. 1). Цикл while выполняется до тех пор, пока верно некое условие. Так как в этом задании отсутствуют закрашенные клетки, то Робот должен определить, находится ли справа от него свободная клетка. Пока такая клетка имеется, Робот переходит на неё: while (FreeFromRight) do Right; 94
Другим признаком продолжения движения вправо может служить отсутствие стены в текущей клетке: while (not WallFromRight) do Right; В любом случае Робот благополучно доберётся до базы (Рис. 2). Рис. 1. Вдоль по коридору 95
Рис. 2. На базе! Задание w2 Задание, подобное предыдущему, но отягощённое необходимостью закрашивать клетки (Рис. 1). Так как число команд Paint равно 11, а число команд Right – 10, то одну команду Paint необходимо выполнить до цикла while: //закрашиваем первую клетку: Paint; while (FreeFromRight) do begin 96
Right; Paint; end; Рис. 1. Надо красить Или после него: while (FreeFromRight) do begin Paint; Right; end; //закрашиваем последниюю клетку: Paint; 97
Задание w3 Это задание отличается от первого только тем, что Робот должен определять не пустые клетки, а стену под ним (Рис. 1). Рис. 1. По стеночке Робот переходит вправо, пока под ним стена: while (WallFromDown) do Right; 98
Задание w4 Практически такое же задание, что и предыдущее, но Робот должен определять закрашенные клетки (Рис. 1). Рис. 1. Осторожно: окрашено! Так как стартовая клетка чистая, то для начала Робот должен перебраться на закрашенную клетку, а дальше двигаться, пока такие клетки не закончатся: Right; while (CellIsPainted) do Right; 99
Задание w5 Теперь Робот находится не перед горизонтальным рядом закрашенных клеток, а в верхнем левом углу прямоугольника из таких клеток (Рис. 1). Рис. 1. Прямо угольно! 100
Решение задачи распадается на две части. Сначала Робот идёт вправо по закрашенным клеткам, а затем вниз. Но здесь важно учесть, что после окончания цикла while Робот окажется за пределами закрашенного прямоугольника, поэтому ему потребуется ещё 1 дополнительный шаг, чтобы вернуться на него: //идём вправо по закрашенным клеткам: while (CellIsPainted) do Right; //возвращаемся на закрашенную клетку: Left; //идём вниз по закрашенным клеткам: while (CellIsPainted) do Down; //возвращаемся на базу: Up; Задание w6 В этом задании Роботу предстоит найти брешь в горизонтальной стене, которая находится ниже стартовой клетки, пройти через неё и добраться до базы в правом нижнем углу (Рис. 1). Нетрудно описать все четыре этапа долгого пути на базу: 1. Робот шагает вниз до стены. 2. Робот идёт вправо до прохода в этой стене. 3. Робот опять идёт вниз до стены поля. 4. Робот идёт до правой стены поля. 101
Рис. 1. Ищите бреши! Или то же самое – в коде: //Робот идёт вниз, пока нет стены: while (FreeFromDown) do Down; //Робот идёт вправо, пока под ним стена: while (WallFromDown) do Right; //Робот идёт вниз, пока нет стены: while (FreeFromDown) do Down; //Робот идёт вправо до стены: while (FreeFromRight) do Right; 102
Задание w7 Робот должен проложить свой путь через закрашенные клетки (Рис. 1). Рис. 1. По всем клеткам Первая закрашенная клетка находится справа от начальной позиции Робота, и он попадёт на неё, если будет шагать вправо, пока находится на чистой клетке: //идём вправо до первой закрашенной клетки: while (CellIsFree) do Right; 103
Первый участок пути пройден: Робот стоит на первой зелёной клетке. Он должен вернуться на чистую клетку, чтобы продолжить свой путь. Из условия задачи известно, что вторая клетка находится выше первой, поэтому сначала Робот делает 1 шаг вверх, а затем идёт в этом направлении, пока не окажется на второй зелёной клетке: //идём вверх ко второй закрашенной клетки: Up; while (CellIsFree) do Up; Здесь он должен изменить направление своего движения и пойти направо: //идём вправо до третьей закрашенной клетки: Right; while (CellIsFree) do Right; Задание w8 В этом задании Роботу предстоит закрасить клетки, расположенные над стеной, причём длина стены заранее неизвестна (Рис. 1). Для нас важно, что база всегда находится у левой стены поля! Так как справа от Робота могут быть закрашиваемые клетки, то он сначала должен пойти вправо. В принципе, он может сразу же закрашивать их, но при движении влево Робот снова окажется на них. Чтобы не закрашивать клетки дважды или не проверять, закрашены ли они уже раньше, Робот просто бодрым строевым шагом идёт вправо, пока не закончится стена под ним: 104
Рис. 1. Красим сверху //идём вправо, пока внизу стена: while (WallFromDown) do Right; Теперь он может идти на базу и по пути весело закрашивать все клетки, которые находятся над стеной: //идём влево до стены и закрашиваем клетки while (FreeFromLeft) do begin Left; 105
//если внизу стена, //закрашиваем клетку: if (WallFromDown) then Paint; end; Задание w9 Эту задачу можно образно назвать так: переход Робота через стену (Рис. 1). Рис. 1. Перевал 106
Стена всегда вертикальная, переменной высоты. Робот стартует от нижней стены поля, а финиширует также у нижней стены, но через стенку. Робот легко поднимется до самой верхней клетки с точкой: //идём вверх, пока справа стена, //и закрашиваем клетки while (WallFromRight) do begin Paint; Up; end; По ходу движения он уже закрасил все клетки, но не самую верхнюю – её он должен закрасить дополнительно: //закрашиваем самую верхнюю клетку: Paint; Робот переваливает через стену и закрашивает верхнюю клетку в следующей вертикали: //идём вправо над стеной: Right; //закрашиваем верхнюю клетку справа: Paint; Дальнейший его путь прост и прям: он идёт до границы поля и закрашивает клетки: //опускаемся до базовой клетки //и закрашиваем клетки: while (FreeFromDown) do begin Down; Paint; end; 107
Задание w10 Это задание – усложнённый вариант предыдущего (Рис. 1). Рис. 1. Двойной перевал Робот должен перебраться через две стены, закрашивая клетки. Стартовая клетка находится в левом нижнем углу поля, а финишная – в правом нижнем. Чтобы воспользоваться алгоритмом предыдущего задания, мы должны переместить Робота к первой стене: //идём вправо до стены: while (FreeFromRight) do 108
Right; Перевал через стену наш Робот уже хорошо освоил: //идём вверх, пока справа стена, //и закрашиваем клетки while (WallFromRight) do begin Paint; Up; end; //закрашиваем самую верхнюю клетку: Paint; //идём вправо над стеной: Right; //закрашиваем верхнюю клетку справа: Paint; //опускаемся до базовой клетки //и закрашиваем клетки: while (FreeFromDown) do begin Down; Paint; end; Легко заметить, что путь до второй стены и её преодоление в точности совпадает с его путём для первой стены. Это значит, что написанную нами часть кода нужно оформить в виде процедуры, чтобы не повторяться. Однако в этом наборе заданий процедуры не предусмотрены, поэтому придётся скопировать весь код. Закончив покрасочные работы, Робот спешит на базу за моральным и увесистым материальным вознаграждением: //Робот идёт вправо до стены //на базу: while (FreeFromRight) do Right; 109
Задание w11 Задание практически идентично w9, но два ряда клеток с точками разделяет горизонтальная стена (Рис. 1). Рис. 1. Разделительная стена Робот обходит стену по уже известному вам алгоритму: //идём вправо, пока внизу стена, //и закрашиваем клетки Right; while (WallFromDown) do begin 110
Paint; Right; end; //закрашиваем самую правую клетку: Paint; //идём вниз: Down; //закрашиваем правую клетку снизу: Paint; //идём влево и закрашиваем клетки: Left; while (WallFromUp) do begin Paint; Left; end; Но в этом задании он ещё должен вернуться на базу в стартовой клетке: //возвращаемся на базу: Paint; Up; Paint; Задание w12 В этом задании Роботу предстоит закрасить 4 стороны прямоугольника (Рис. 1). Робот может выбрать 2 направления обхода – по часовой стрелке и против неё. Нам, конечно, более привычен первый вариант, поэтому закрашиваем начальную клетку и делаем шаг вправо, чтобы под Роботом оказалась стена. //закрашиваем верхнюю сторону прямоугольника: Paint; Right; 111
Рис. 1. Прямоугольный обход Теперь Робот идёт вправо и закрашивает клетки: while (WallFromDown) do begin Paint; Right; end; В результате этих действий Робот окажется в самой правой клетке верхней стороны прямоугольника. Дальше он обходит стены по тому же сценарию, что и раньше: 112
//закрашиваем правую сторону прямоугольника: Paint; Down; while (WallFromLeft) do begin Paint; Down; end; //закрашиваем нижнюю сторону прямоугольника: Paint; Left; while (WallFromUp) do begin Paint; Left; end; Когда Робот вернётся на базу, то попадёт на уже закрашенную клетку: //закрашиваем левую сторону прямоугольника: Paint; Up; while (WallFromRight) do begin Paint; Up; end; Задание w13 Невероятно простое задание, в котором Робот должен подниматься вверх, пока с двух сторон от него находятся стены (Рис. 1). Решение просто повторяет условие задачи: while (WallFromLeft and WallFromRight) do Up; 113
Рис. 1. Между двух стен Задание w14 Почти такое же задание, но Робот должен подниматься, пока слева и справа от него имеется хотя бы одна стена (Рис. 1). Опять достаточно переписать условие задачи на паскаль: while (WallFromLeft or WallFromRight) do Up; 114
Рис. 1. Разновысотные стены Задание w15 Очередное задание на сложное логическое условие (Рис. 1). Важно правильно сформулировать его. По условию задачи, Робот должен идти вправо до тех пор, пока под ним стена или незакрашенная клетка: //пока внизу стена или //чистая клетка, //идём вправо: 115
while (WallFromDown or CellIsFree) do Right; Рис. 1. Будет сложно Когда условие, записанное в скобках, станет ложным, Робот будет стоять в первой закрашенной клетке, под которой нет стены. Тогда он должен сделать шаг вниз и закрасить базовую клетку: //опускаемся на клетку ниже и //закрашивае её: Down; Paint; 116
Задание w16 На этот раз Робота ожидает сложная работа (Рис. 1)! Рис. 1. Ещё сложнее Было бы неразумно поочерёдно обходить все стены, чтобы найти последнюю. Правильный способ передвижения такой. Поднимаемся вверх на 1 клетку (или опускаемся) и обходим забор сверху – пока не упрёмся в правую стену поля: //делаем шаг вверх: Up; //и идём до правой стены: while (not WallFromRight) do 117
Right; Возвращаемся на свою горизонталь и идём вправо до забора: //опускаемся на горизонталь с забором: Down; //и идём влево до стены: while (not WallFromLeft) do Left; Задание w17 По сравнению с предыдущим заданием изменился только вид забора (Рис. 1). Исходный же код остался без изменений: //делаем шаг вверх: Up; //и идём до правой стены: while (not WallFromRight) do Right; //опускаемся на горизонталь с забором: Down; //и идём влево до стены: while (not WallFromLeft) do Left; 118
Рис. 1. Тех же щей, да погуще влей! 119
Набор заданий cif В этом наборе 22 задания на циклы и условные операторы (Рис. 1). Рис. 1. Придётся потрудиться Задание cif1 Робот идёт вправо до упора и закрашивает клетки, над которыми нависает стена (Рис. 1). Робот проверяет в цикле while, не дошёл ли он до стены справа: //идём вправо до стены: while (FreeFromRight) do begin Right; И каждую клетке, над которой имеется стена, Робот закрашивает: //если сверху стена, //закрашиваем клетку: if (WallFromUp) then Paint; 120
end; Рис. 1. Красим под стенами Задание cif2 Это задание отличается от предыдущего только тем, что стены могут быть и сверху, и снизу (Рис. 1). Немного усложняем логическое условие в операторе if – и задача решена: 121
//идём вправо до стены: while (FreeFromRight) do begin Right; //если сверху или снизу стена, //закрашиваем клетку: if (WallFromUp or WallFromDown) then Paint; end; Рис. 1. Двухстенник 122
Задание cif3 В этом задании мы встречаем старые стены, но теперь Робот должен закрашивать только те клетки, которые находятся между двух стен (Рис. 1). Рис. 1. Простенки Чтобы решить задачу, достаточно изменить логическое условие: //идём вправо до стены: while (FreeFromRight) do begin Right; //если сверху и снизу стена, //закрашиваем клетку: 123
if (WallFromUp and WallFromDown) then Paint; end; Задание cif4 Напряжение нарастает! Робот должен закрасить только те клетки, над которыми есть стена, а под ними стены нет (Рис. 1). Рис. 1. Снизу пусто, а сверху - стена И опять всё решает небольшое изменение в логическом условии: 124
//идём вправо до стены: while (FreeFromRight) do begin Right; //если сверху стена, //а снизу нет, //то закрашиваем клетку: if (WallFromUp and FreeFromDown) then Paint; end; Задание cif5 Ну очень «сложное» задание! Робот должен закрасить только такие клетки, для которых не выполняется условие: если сверху есть стена, а снизу нет (Рис. 1). Рис. 1. Очень сложно 125
Это условие уже не столь просто для понимания, как предыдущие, поэтому над ним нужно подумать: //идём вправо до стены: while (FreeFromRight) do begin Right; //если неверно, что сверху стена, //а снизу нет, //то закрашиваем клетку: if not (WallFromUp and FreeFromDown) then Paint; end; Задание cif6 Задание такое: закрасить клетки, которые не лежат между двух стен (Рис. 1). Рис. 1. Меньше двух стен 126
Условие закрашивания клеток такое: сверху или снизу от клетки свободно: //идём вправо до стены: while (FreeFromRight) do begin Right; //если сверху или снизу, //нет стены, //то закрашиваем клетку: if (FreeFromDown or FreeFromUp) then Paint; end; Задание cif7 Жуткая картина (Рис. 1)! Здесь уже одним логическим оператором не обойтись – их потребуется пара. Если клетка сверху свободна, то Робот делает шаг вверх и закрашивает её: //идём вправо до стены: while (FreeFromRight) do begin Right; //если сверху нет стены, //то закрашиваем клетку сверху: if (FreeFromUp) then begin Up; Paint; //возвращаемся на свою горизонталь: Down; end; 127
Рис. 1. Ужас! Важно после каждого подъёма возвращаться на свою горизонталь! Отсутствие стены снизу Робот обрабатывает аналогично: //если снизу нет стены, //то закрашиваем клетку снизу: if (FreeFromDown) then begin Down; Paint; Up; 128
end; end; Задание cif8 Ещё одно задание на 2 логических условия (Рис. 1). Рис. 1. Двойная логика На Рис. 1⬆ хорошо видно, что: 1. Робот должен закрасить клетку сверху, если стена снизу. 129
2. Робот должен закрасить клетку снизу, если стена сверху. //идём вправо до стены: while (FreeFromRight) do begin Right; //если снизу есть стена, //то закрашиваем клетку сверху: if (WallFromDown) then begin Up; Paint; //возвращаемся на свою горизонталь: Down; end; //если сверху есть стена, //то закрашиваем клетку снизу: if (WallFromUp) then begin Down; Paint; Up; end; end; Задание cif9 Это задание – усложнённый вариант предыдущего. Здесь Робот должен ещё закрашивать клетки между двумя горизонтальными стенами (Рис. 1). Чтобы справиться с заданием, Робот должен после каждого шага вправо проверять наличие двух стен – сверху и снизу: //идём вправо до стены: while (FreeFromRight) do begin Right; if (WallFromDown and WallFromUp) then 130
begin Paint; End Рис. 1. Вариант! Если стен меньше двух, Робот действует, как в Задании cif8: //если снизу есть стена, //то закрашиваем клетку сверху: else if (WallFromDown) then begin Up; Paint; //возвращаемся на свою горизонталь: 131
Down; end //если сверху есть стена, //то закрашиваем клетку снизу: else if (WallFromUp) then begin Down; Paint; Up; end; end; Задание cif10 Робот должен закрасить клетки, которые находятся ниже изначально закрашенных, если под ними нет стены (Рис. 1). Алгоритм решения задачи простой и прямо вытекает из условия: //идём вправо до стены: while (FreeFromRight) do begin Right; //Робот закрашивает клетку, //которая находится ниже закрашенной //и под которой чистая клетка: if (CellIsPainted and FreeFromDown) then begin Down; Paint; Up; end; end; 132
Рис. 1. Красим снизу Задание cif11 В этом задании Робот должен закрасить клетку, если одновременно выполняются 3 условия, поэтому логические выражения соединяются двумя операторами and (Рис. 1). Добавляем в исходный код Задания cif10 ещё одно логическое условие – и задача решена: //идём вправо до стены: 133
while (FreeFromRight) do begin Right; //Робот закрашивает клетку, //которая находится ниже закрашенной //под которой чистая клетка, //а сверху - стена: if (CellIsPainted and FreeFromDown and WallFromUp) then begin Down; Paint; Up; end; end; Рис. 1. И - И 134
Задание cif12 Нам необходимо хорошенько проанализировать ситуацию на поле, чтобы вывести логические условия для закрашивания клеток (Рис. 1). Рис. 1. Анализируй всё! 1. Клетки закрашиваются только выше горизонтали Робота. 2. Над ними нет стены. 3. Клетка с Роботом закрашенная. 4. Если клетка с Роботом чистая, то под ней не должно быть стены. На этом разработку алгоритма можно считать законченной, и мы просто записываем его на языке паскаль: 135
//идём вправо до стены: while (FreeFromRight) do begin Right; //Робот закрашивает клетку сверху, //если там нет стены: if (FreeFromUp and (CellIsPainted or (CellIsFree and FreeFromDown))) then begin Up; Paint; Down; end; end; Задание cif13 Это задание также на составление сложного логического выражения (Рис. 1). Рис. 1. Не бойтесь сложностей! 136
Анализируем позицию на поле: 1. Робот закрашивает клетку над собой. 2. Над клеткой с Роботом не должно быть стены. 3. Если он стоит в закрашенной клетке, то под ней должна быть стена. 4. В противном случае под ней не должно быть стены. Условие 2 верно для всех клеток на горизонтали Робота, поэтому его можно не проверять. Остаётся записать условия 3 и 4: //идём вправо до стены: while (FreeFromRight) do begin Right; //Робот закрашивает клетку сверху, //если он стоит в закрашенной клетке И //под ним стена ИЛИ //Робот стоит в незакрашенной клетке И //под ним нет стены: if (CellIsPainted and WallFromDown) or FreeFromDown) then begin Up; Paint; Down; end; end; (CellIsFree and Задание cif14 Задание довольно простое, если правильно понять условие закрашивания клеток (Рис. 1). Легко видеть, что Робот закрашивает клетки, которые находятся под закрашенными. Однако, находясь на своей горизонтали, Робот не может определить цвет клетки ниже, поэтому он должен сделать шаг вниз, а это действие возможно только если под его клеткой нет стены. 137
Рис. 1. Проводим условные операции Алгоритм готов: //идём вправо до стены: while (FreeFromRight) do begin Right; //если снизу нет стены, //Робот делает шаг вниз: if (FreeFromDown) then begin Down; //если он оказался в закрашенной клетке, //то делает ещё шаг вниз и 138
//закрашивает клетку: if (CellIsPainted) then begin Down; Paint; //Робот возвращается на //свою горизонталь: Up; end; Up; end; end; Здесь важно учесть, что после закрашивания клетки Робот должен сделать 2 шага вверх, а без закрашивания – только 1. Задание cif15 Задача несложная, но требует внимания при прокладывании маршрута Робота (Рис. 1). Рис. 1. Прокладываем пути 139
Робот сначала делает шаг вверх (или вниз). Если там закрашенная клетка, то он должен проверить и нижнюю клетку. Для этого он делает 2 шага вниз. Если и вторая клетка закрашенная, то он поднимается в свою горизонталь и закрашивает клетку. Если же нижняя клетка чистая, то он просто поднимается в свою горизонталь. И наконец, если верхняя клетка оказалась чистой, то он возвращается в свою горизонталь, делая шаг вниз: //идём вправо до стены: while (FreeFromRight) do begin Right; //если снизу и сверху закрашенные клетки, //то Робот закрашивает клетку, //на которой стоит: Up; if (CellIsPainted) then begin Down; Down; if (CellIsPainted) then begin Up; Paint; end else Up; end //Робот возвращается на свою горизонталь: else Down; end; Задание cif16 Подобную задачу мы уже решали в Задании if3, но там поле было маленькое и одного размера. Сейчас же Роботу предстоит перейти в противоположный угол на поле произвольного размера (Рис. 1). 140
Рис. 1. Поле произвола Проще всего сначала определить угол, в котором находится Робот: //направление на стены: var dir := 0; if (WallFromUp) then dir += 1; if (WallFromDown) then dir += 2; if (WallFromLeft) then dir += 4; if (WallFromRight) then dir += 8; 141
А вот теперь его легко провести по стеночкам на базу. Если он стоит у левой стены, то идёт вправо до упора. Если у правой, то влево: //Робот у левой стены: if ((dir = 5) or (dir = 6)) then begin while(FreeFromRight) do Right; end; //Робот у правой стены: if ((dir = 9) or (dir = 10)) then begin while(FreeFromLeft) do Left; end; Затем Робот аналогично выбирает направление перехода в зависимости от того, у какой второй стены он находится – у нижней или у верхней: //Робот у нижней стены: if ((dir = 6) or (dir = 10)) then begin while(FreeFromUp) do Up; end; //Робот у верхней стены: if ((dir = 5) or (dir = 9)) then begin while(FreeFromDown) do Down; end; Можно и не определять положение Робота, а сразу шагать в свободную сторону до стены: 142
//если справа свободно, //идём вправо до стены: if (FreeFromRight) then while(FreeFromRight) do Right //если слева свободно, //идём влево до стены: else if (FreeFromLeft) then while(FreeFromLeft) do Left; //если сверху свободно, //идём вверх до стены: if (FreeFromUp) then while(FreeFromUp) do Up //если снизу свободно, //идём вниз до стены: else if (FreeFromDown) then while(FreeFromDown) do Down; Задание cif17 Задание: пройти по коридору до конца (Рис. 1). У коридора обязательно есть вертикальная часть, которую необходимо пройти в первую очередь: //Робот идёт вверх до упора: while (FreeFromUp) do Up; Затем коридор может повернуть направо или налево (или вообще не повернуть). 143
Рис. 1. Кривая дорожка Направление поворота легко определить по отсутствию стены. Если стена отсутствует слева, то и Робот должен идти влево: //в какую сторону поворачивает коридор? if (FreeFromLeft) then //влево while (FreeFromLeft) do Left Если же отсутствует стена справа, то и Робот марширует вправо: else if (FreeFromRight) then //вправо 144
while (FreeFromRight) do Right; Если все стены на месте, то там же и Робот: //не поворачивает Задание cif18 Лёгкая прогулка для Робота (Рис. 1). Рис. 1. Большая прогулка 145
Алгоритм простой! 1. Робот шагает строго направо. 2. После каждого шага определяет цвет своей клетки. 3. Если она белая, то верхний ряд закончился, и Робот на базе. 4. Если зелёная, то Робот делает шаг вниз, чтобы определить цвет нижней клетки. 5. Если она белая, значит, закончился нижний ряд. 6. Если зелёная, то Робот поднимается на свою горизонталь и продолжает шагать вправо. //Робот идёт вправо: while (FreeFromRight) do begin Right; //короткий ряд верхний: if (CellIsFree) then begin //он уже на базе: break; end //иначе он проверяет клетку снизу: else begin Down; if (CellIsFree) then //он уже на базе: break //возвращается в свою горизональ: else Up; end; end; Задание cif19 База находится сразу после окончания более короткой стены (Рис. 1). 146
Рис. 1. Измеряем стены В этой ситуации Робот может действовать несколькими способами. Например, каждый шаг проверять, не закончилась ли одна из стен. Но наш Робот действует иначе. Он идёт вправо, пока под ним нижняя стена. В итоге он окажется в первой свободной клетке своей горизонтали: //Робот идёт до конца нижней стены: while (WallFromDown) do Right; //нижняя стена закончилась 147
Если нижняя стена короче, то он будет на базе. Но Робот не умеет определять клетку с базой, поэтому он должен проверить, не закончилась ли раньше верхняя стена. Для этого он шагает вверх на 1 клетку и, если над ним нет стены, он идёт влево, пока она не появится. После этого он должен сделать шаг вправо, чтобы оказаться на базе. Если сразу после шага вверх над Роботом окажется стена, значит, верхняя стена длиннее, и он делает шаг вниз, чтобы вернуться на базу: //проверяем верхнюю: Up; if (FreeFromUp) then begin while (FreeFromUp) do Left; //на базе: Right; end //возвращаемся на базу: else Down; Задание cif20 Предыдущее задание, но наоборот: Робот должен дойти до базы после длинной стены (Рис. 1). Забота для Робота даже проще, чем предшествующая. Сначала он идёт до конца нижней стены, а затем поднимается вверх и проверяет верхнюю: //Робот идёт до конца нижней стены: while (WallFromDown) do Right; //нижняя стена закончилась 148
//проверяем верхнюю: Up; Рис. 1. Удлиняем Если над ним стена, значит, верхняя стена длиннее, и он идёт вправо до базы: if (WallFromUp) then begin while (WallFromUp) do Right; //на базе end 149
Если же верхняя стена короче, то Робот спускается на базу: //возвращаемся на базу: else Down; Задание cif21 В горизонтали с Роботом находится закрашенная клетка – справа или слева от него. Он должен найти её и идти вниз до второй закрашенной клетки (Рис. 1). 150
Рис. 1. Ищем вторую клетку Робот может сначала пойти влево. Если он не найдёт закрашенную клетку, то пойдёт вправо. В любом случае он найдёт первую закрашенную клетку и пойдёт вниз до второй закрашенной клетки: //верное направление: var dir := false; //пока слева чистые клетки, //Робот идёт влево: while (FreeFromLeft) do begin Left; if (CellIsPainted) then begin dir := true; break; end end; //первая закрашенная клетка - справа: if (not dir) then //Робот идёт вправо: while (FreeFromRight and CellIsFree) do Right; 151
//Робот идёт вниз до второй //закрашенной клетки: Down; while (FreeFromDown and CellIsFree) do begin Down; end; Флаг dir показывает, нашёл ли Робот закрашенную клетку слева, чтобы не искать её справа. Задание cif22 Самое сложное задание в этом наборе! Робот должен выбраться из коридора и добраться до базы в левом верхнем углу поля (Рис. 1). Рис. 1. Выход должен быть! 152
Выход может быть слева, справа, снизу или сверху (Рис. 2). Рис. 2. Возможны варианты! 153
Так как выход может быть в четырёх направлениях, в зависимости от чего Робот должен проложить свой путь на базу, то ему предстоит большая работа по исследованию «лабиринта»! Прежде всего, нам потребуется переменная dir, в которую мы будем записывать направление выхода: 1 – выход слева 2 – выход снизу 3 – выход сверху 4 – выход справа 0 – выход не найден //направление выхода: var dir := 0; В данном случае Робот может пойти сначала влево, потом вправо, а может и наоборот. Пусть он идёт влево. Тогда: 1. Если над ним и под ним нет стен, значит, выход слева. 2. Если под ним нет стены, значит, выход снизу. 3. Если над ним нет стены, значит, выход сверху. И так он продолжает шагать влево, пока не упрётся в стену или не найдёт выход: //Робот идёт влево: while (true) do begin if (FreeFromDown and FreeFromUp) then begin dir := 1; break; end else if (FreeFromDown) then begin 154
dir := 2; break; end else if (FreeFromUp) then begin dir := 3; break; end else if (FreeFromLeft) then Left else break; end; Если слева выхода нет, то Робот аналогично исследует правую часть коридора: //Робот идёт вправо: if (dir = 0) then while (FreeFromRight) do begin Right; if (FreeFromDown and FreeFromUp) then begin dir := 4; break; end else if (FreeFromDown) then begin dir := 2; break; end else if (FreeFromUp) then begin dir := 3; break; end end; Теперь Робот знает направление выхода и стоит как раз перед ним. 155
Если выход слева, то он идёт влево до стены поля, а затем поднимается вверх: //выход слева: if (dir = 1) then begin while (FreeFromLeft) do Left; while (FreeFromUp) do Up; end Если выход снизу, то Робот опускается до нижней стены поля, идёт влево до стены и поднимается на базу: //выход снизу: else if (dir = 2) then begin while (FreeFromDown) do Down; while (FreeFromLeft) do Left; while (FreeFromUp) do Up; end Аналогично Робот действует и в тех случаях, когда выход сверху или справа: //выход сверху: else if (dir = 3) then begin while (FreeFromUp) do Up; while (FreeFromLeft) do Left; end //выход справа: 156
else begin while (FreeFromUp) do Up; while (FreeFromLeft) do Left; end 157
Набор заданий count В этом наборе – 17 заданий (Рис. 1). Рис. 1. Считать – не пересчитать! В них Роботу придётся считать свои шаги, а для этого ему потребуется переменная. Задание count1 Самое простое задание на подсчёт шагов (Рис. 1). Для подсчёта шагов Робот использует переменную step, значение которой сначала равно нулю: //число шагов: var step := 0; По условию задачи, Робот должен дойти до правой стены: //Робот идёт вправо до стены и //считает шаги: while (FreeFromRight) do 158
begin Right; step += 1 end; Рис. 1. Программирование любит счёт Клетку у стены он закрашивает: //Робот закрашивает клетку: Paint; 159
Чтобы вернуться на прежнее место, Робот должен пройти влево столько же шагов, сколько он сделал вправо: //Робот возвращается на базу: while (step > 0) do begin step -= 1; Left; end; Задание count2 Более сложное задание на 2 счётчика: Робот должен дойти до правого нижнего угла, закрасить там клетку и вернуться на базу (Рис. 1). Рис. 1. Двойной счётчик 160
Код программы легко написать, учитывая, что Робот последовательно перемещается в двух направлениях – вправо и вниз: //число шагов вправо: var stepRight := 0; //число шагов вниз: var stepDown := 0; //Робот идёт вправо до стены и //считает шаги: while (FreeFromRight) do begin Right; stepRight += 1 end; //Робот идёт вниз до стены и //считает шаги: while (FreeFromDown) do begin Down; stepDown += 1 end; //Робот закрашивает клетку: Paint; //Робот возвращается на базу: while (stepRight > 0) do begin stepRight -= 1; Left; end; while (stepDown > 0) do begin stepDown -= 1; Up; end; 161
Задание count3 Задание, аналогичное предыдущему, но база расположена симметрично относительно нисходящей диагонали квадрата (Рис. 1). Рис. 1. Симметрично Робот всегда стартует с клетки, которая ниже и левее финишной! Робот может выбрать направление движения – либо вправо, либо вверх. Пусть он идёт вправо и считает шаги до закрашенной клетки: //число шагов вправо: 162
var stepRight := 0; while (CellIsFree) do begin stepRight += 1; Right; end; Так как закрашенная клетка лежит симметрично относительно стартовой, то Робот должен сделать столько же шагов вверх, сколько он сделал вправо: //Робот идёт на базу: while (stepRight > 0) do begin stepRight -= 1; Up; end; Задание count4 Робот должен прийти на базу, которая находится под стартовой клеткой, но её отделяет от стартовой клетки горизонтальная стена (Рис. 1). Робот всегда обходит стену по часовой стрелке. Это задание – лёгкая прогулка для нашего подопечного. Он идёт вправо, пока под ним не закончится стена, добросовестно считая при этом каждый свой шаг. Затем делает шаг вниз и делает столько же шагов в противоположном направлении: //число шагов вправо: var stepRight := 0; //Робот идёт вправо, //пока под ним стена: 163
while (WallFromDown) do begin stepRight += 1; Right; end; //Робот делает шаг вниз: Down; //Робот идёт на базу: while (stepRight > 0) do begin stepRight -= 1; Left; end; Рис. 1. За стеной 164
Задание count5 Роботу предстоит совершить «кругосветное» путешествие, закрашивая по ходу движения клетки (Рис. 1). Рис. 1. Вокруг да около стены Клетки по периметру квадрата мы уже закрашивали, но теперь Робот занимает случайную стартовую позицию на поле. Поэтому, прежде всего, Робота нужно установить, например, в верхний левый угол поля, тогда он без труда закрасит все нужные клетки, обходя их по часовой стрелке (или в обратном направлении). Но ему ещё предстоит вернуться в начальную позицию, поэтому при перемещении в угол Робот должен отдельно считать шаги влево и вверх: 165
var stepLeft := 0; var stepUp := 0; while (FreeFromLeft) do begin stepLeft += 1; Left; end; while (FreeFromUp) do begin stepUp += 1; Up; end; //Робот закрашивает верхнюю сторону: while (FreeFromRight) do begin Paint; Right; end; //Робот закрашивает правую сторону: while (FreeFromDown) do begin Paint; Down; end; //Робот закрашивает нижнюю сторону: while (FreeFromLeft) do begin Paint; Left; end; //Робот закрашивает левую сторону: while (FreeFromUp) do begin Paint; Up; end; 166
Чтобы вернуться на место, Робот должен сделать столько шагов вправо, сколько он сделал влево, и столько шагов вниз, сколько он сделал вверх: //Робот возвращается на базу: while (stepLeft> 0) do begin stepLeft -= 1; Right; end; while (stepUp > 0) do begin stepUp -= 1; Down; end; Задание count6 Робот мечется между двумя стенами в попытках найти ближнюю (Рис. 1). Чтобы определить, какая из двух стен ближе к стартовой позиции, Робот идёт сначала влево, а затем вправо, считая шаги: //число шагов влево: var stepLeft := 0; //число шагов вправо: var stepRight := 0; //Робот идёт влево: while (FreeFromLeft) do begin stepLeft += 1; Left; end; //Робот идёт вправо: while (FreeFromRight) do begin stepRight += 1; Right; end; 167
Рис. 1. Буриданов Робот Так как он считает шаги вправо не от начальной клетки, а от левой стены, то нужно вычислить расстояние от начальной клетки до правой стены: //до левой стены stepLeft клеток, //а до правой: stepRight -= stepLeft; Если правая стена окажется ближе, чем левая, то Роботу ничего не нужно делать, ведь он уже на базе. Если ближе левая стена, то он сможет пройти до неё нужное число шагов: 168
//если левая стена ближе, //Робот идёт к ней: if (stepLeft < stepRight) then begin stepLeft += stepRight; while (stepLeft > 0) do begin stepLeft -= 1; Left; end; end; //если ближе правая стена, //то он уже на месте Но теперь шаги считать необязательно, поэтому Робот может просто шагать влево, пока не упрётся в стену: if (stepLeft < stepRight) then while (FreeFromLeft) do Left; Задание count7 Робот должен обогнуть крючкообразную стену и дойти до базовой клетки, которая находится под стартовой, но их разделяет горизонтальная стена (Рис. 1). Весьма хитроумное задание, над которым придётся поломать голову! Вертикальная стена может препятствовать победному шествию нашего Робота как справа, так и слева. Робот может начать поиск стены с любого направления. Например, он пошёл влево: var step := 0; //СТЕНА СПРАВА? 169
//Робот идёт влево: while (FreeFromLeft) do begin step += 1; Left; Рис. 1. Стены разделяют 170
Если на каком-то шаге под ним не окажется стены, значит, слева стены тоже нет, поэтому Робот шагает вниз на 1 клетку, затем делает step шагов вправо – и он на базе: //стены снизу нет --> //стены слева нет: if (FreeFromDown) then begin //Робот делает шаг вниз и //идёт направо - на базу: Down; while (step > 0) do begin step -= 1; Right; end; exit; end; end; Если Робот упёрся в левую стену, то он идёт вправо, пока под ним не закончится стена: //СТЕНА СЛЕВА //Робот идёт вправо: while (WallFromDown) do begin step -= 1; Right; end; После этого он идёт вниз, а затем влево – до базы: Down; while (step < 0) do begin step += 1; Left; end; 171
Задание count8 Робот должен остановиться на пятой закрашенной клетке, которая находится справа от стартовой позиции (Рис. 1). Рис. 1. Клетки по пять считают При разработке алгоритма важно учесть, что: 1. Робот может сразу стоять на закрашенной клетке, поэтому сначала он считает клетки, а затем идёт вправо. 2. Робот должен сразу же прекратить движение, если окажется на пятой закрашенной клетке: 172
//счётчик закрашенных клеток: var n := 0; //Робот идёт вправо и считает закрашенные клетки: while (true) do begin if (CellIsPainted) then n += 1; //Робот стоит на пятой закрашенной клетке //задание выполнено: if (n = 5) then break; Right; end; Задание count9 Задание, очень похожее на предыдущее, но Робот считает только соседние закрашенные клетки (Рис. 1). Рис. 1. Считаем соседей 173
Код практически не изменился, но каждая незакрашенная клетка сбрасывает счётчик: //счётчик закрашенных клеток: var n := 0; //Робот идёт вправо и считает закрашенные клетки: while (true) do begin if (CellIsPainted) then n += 1 //обнуляем счётчик, если //встретилась незакрашенная клетка: else n := 0; //Робот стоит на пятой закрашенной клетке //задание выполнено: if (n = 3) then break; Right; end; Задание count10 Робот должен закрасить клетки, под которыми находится стена. Но если стена закончилась, а клеток закрашено меньше пяти, то Робот «докрашивает» оставшиеся (Рис. 1). В этом задании важно правильно сформулировать условие перемещения Робота вправо: Пока под ним стена ИЛИ закрашено меньше пяти клеток: //счётчик закрашенных клеток: var n := 0; //Робот идёт вправо и считает закрашенные клетки: while (WallFromDown or (n < 5)) do 174
begin Paint; n += 1; Right; end; Рис. 1. Опять пять Задание count11 Роботу предстоит определить длину тупика и, если его длина больше 4 клеток, то закрасить стартовую клетку, (Рис. 1). 175
Рис. 1. Путь в тупик Роботу не обязательно идти до конца коридора. Если он насчитает 5 клеток, то дальнейшие его шаги вправо ничего не изменят: //счётчик клеток: var n := 0; //Робот идёт вправо и считает клетки: while (FreeFromRight and (n < 5)) do begin n += 1; Right; end; 176
Робот в любом случае должен вернуться в исходную клетку: //Робот возвращается //на первую клетку: for var i := 1 to n-1 do Left; Если Робот насчитал в коридоре не менее 5 клеток, то он закрашивает первую клетку: //Робот закрашивает первую //клетку коридора: if (n > 4) then Paint; Задание count12 Робот идёт на базу в конце коридора и закрашивает клетки через одну (Рис. 1). Рис. 1. Чередуем 177
Нам удобнее считать клетки, начиная с единицы, поэтому пусть Робот сначала занимает первую клетку: //счётчик клеток: var n := 1; Теперь Робот может смело шагать вправо по коридору, как Штирлиц в сериале Семнадцать мгновений весны, и считать клетки. Все чётные клетки он добровольно и добросовестно закрашивает: while (FreeFromRight) do begin Right; n += 1; //чётные клетки Робот закрашивает: if (n mod 2 = 0) then Paint; end; Задание count13 В этом задании Робот закрашивает нечётные клетки (Рис. 1). Казалось бы, нужно просто изменить условие в операторе if, но на самом деле задача коварнее! Робот сразу стоит в клетке, которую требуется закрасить: //счётчик клеток: var n := 1; //Робот закрашивает стартовую клетку: Paint; А вот дальше он идёт вперёд, не ведая забот: 178
while (FreeFromRight) do begin //шаг вправо: Right; n += 1; //нечётные клетки Робот закрашивает if (n mod 2 = 1) then Paint; end; Рис. 1. По нечётным 179
Задание count14 Задача перед Роботом стоит практически такая же, как и в Задании count13, но он должен закрашивать каждую третью клетку (Рис. 1). Рис. 1. Через две на третью Код окончательный и обжалованию не подлежит: //счётчик клеток: var n := 1; while (FreeFromRight) do begin 180
Right; n += 1; //каждую третью клетку Робот закрашивает: if (n mod 3 = 0) then Paint; end; Задание count15 А это задание повторяет count14, но закрашивать Роботу нужно каждую третью клетку по ходу своего движения вправо (Рис. 1). Рис. 1. Проторёнными путями 181
Код оставляем без дополнительных комментариев: //счётчик клеток: var n := 1; //Робот закрашивает стартовую клетку: Paint; while (FreeFromRight) do begin //шаг вправо: Right; n += 1; //нечётные клетки Робот закрашивает if (n mod 3 = 1) then Paint; end; Задание count16 Интересное задание, в котором Робот должен закрасить клетки нечётных пролётов (Рис. 1). Номер пролёта совпадает с числом пройденных Роботом закрашенных клеток. Сначала идёт нулевой пролёт: //счётчик пролётов: var n := 0; Когда Роботу встретится первая закрашенная клетка (а это может быть и стартовая клетка!), счётчик пролётов увеличится, то есть после нулевого следует первый пролёт: while (FreeFromRight) do begin 182
//Робот на закрашенной клетке: if (CellIsPainted) then begin n += 1; Right; end; Рис. 1. Пролёты Сейчас Робот стоит в клетке, следующей за закрашенной. Если номер пролёта нечётный, то он закрашивает клетку: //Робот идёт по нечётному пролёту: if (n mod 2 = 1) then 183
begin Paint; end; Затем он в любом пролёте делает шаг вправо: Right; Если очередная клетка – закрашенная, то изменяется номер пролёта, в противном случае Робот идёт по тому же пролёту, то есть в нечётном пролёте он закрашивает клетки, а в чётном не закрашивает: end; Когда Робот дойдёт до конечной клетки поля, то её он также должен закрасить, если она заканчивает нечётный пролёт: //последняя клетка нечётного пролёта: if (n mod 2 = 1) then Paint; Задание count17 Простое логическое упражнение для Робота: он должен остановиться в клетке, которая нарушает общую закономерность (Рис. 1). Прежде всего, нужно найти закономерность в расстановке стен: 1. Под всеми нечётными клетками должна быть стена. 2. Под всеми чётными клетками стены быть не должно. 184
Рис. 1. Закономерно Робот переступает одиночными шагами вправо и считает клетки: //счётчик клеток: var n := 0; while (FreeFromRight) do begin n += 1; Если под нечётной клеткой отсутствует стена, Робот останавливается: 185
if ((n mod 2 = 1) and FreeFromDown) then break Точно так же он поступает, если под чётной клеткой оказывается стена: else if ((n mod 2 = 0) and WallFromDown) then break; Right; end; 186
Набор заданий cc Следующий набор состоит из 19 заданий на вложенные циклы (Рис. 1). Рис. 1. Укладываем циклы Задание cc1 Робот должен закрасить клетки, расположенные ступеньками (Рис. 1). Размеры поля и ступенек постоянные, поэтому записать вложенные циклы совсем нетрудно - достаточно посчитать число ступенек и число клеток в каждой из них: //Робот шагает по ступенькам: for var j := 1 to 6 do begin //Робот закрашивает ступеньку: for var i := 1 to 5 do begin Paint; Right; end; //Робот спускается на следующую ступеньку: 187
Down; end; Рис. 1. По ступеням Задание cc2 Это задание сложнее – длина каждой следующей ступеньки увеличивается на единицу (Рис. 1). 188
Рис. 1. Ступеньки удлиняются Легко заметить, что длина ступеньки в точности равна её номеру, поэтому нужно исправить только диапазон изменения переменной цикла i в предыдущем коде: //Робот шагает по ступенькам: for var j := 1 to 6 do begin //Робот закрашивает ступеньку: for var i := 1 to j do begin Paint; Right; end; 189
//Робот спускается на следующую ступеньку: Down; end; Задание cc3 И в этом задании ступеньки имеют переменную длину, но на этот раз они укорачиваются (Рис. 1). Рис. 1. Короче! 190
Опять нужно исправить только заголовок второго цикла, чтобы Робот успешно справился с заданием: //Робот шагает по ступенькам: for var j := 1 to 6 do begin //Робот закрашивает ступеньку: for var i := 7-j downto 1 do begin Paint; Right; end; //Робот спускается на следующую ступеньку: Down; end; Задание cc4 Задание cc4 предлагает Роботу пройти по извилистому пути и закрасить очень много клеток (Рис. 1). Первые клетки каждой горизонтали Робот закрашивает во внешнем цикле for с переменной row: for var row := 1 to 9 do begin //Робот закрашивает первую //клетку горизонтали: Paint; 191
Рис. 1. Объёмы растут Остальные клетки горизонтали Робот должен пройти дважды, поэтому он может сначала бодро и весело дошагать до правой стены, а при возвращении к левой стене закрашивать клетки (или наоборот): //Робот идёт до правой стены: for var col := 1 to 10 do Right; //Робот возвращается к левой стене //и закрашивает клетки: for var col := 1 to 10 do begin Paint; Left; 192
end; //Робот переходит на горизонталь ниже: Down; end; Задание cc5 И вот наш Робот добрался почти до настоящего лабиринта (Рис. 1)! Рис. 1. Даёшь лабиринт! 193
В отличие от предыдущего задания, Робот возвращается по другой горизонтали: for var row := 1 to 4 do begin //Робот идёт до правой стены: for var col := 1 to 13 do Right; //Робот переходит на горизонталь ниже: Down; //Робот возвращается к левой стене: for var col := 1 to 13 do Left; //Робот переходит на горизонталь ниже: Down; end; Не ошибитесь: всего Робот должен выполнить 4 челночных прохода! Задание cc6 Робот получает под покраску 5 комнат (Рис. 1). Задание очень простое, но требует внимания. До первой двери Роботу нужно сделать 3 шага вправо, а до остальных 4: //Робот пройдёт по 5 комнатам: for var n := 1 to 5 do begin //Робот идёт вправо до входа: for var col := 1 to 3 do Right; //Робот поднимается до верхне стены: for var row := 1 to 9 do Up; 194
//и закрашивает клетку: Paint; //Робот возвращается вниз: for var row := 1 to 9 do Down; Рис. 1. Пятикомнатное задание Но этот добавочный шаг не следует выполнять у правой границы поля: //и делает 1 шаг вправо: if (n < 5) then Right; end; 195
Задание cc7 Вариант ступенек с возвратами (Рис. 1). Рис. 1. Вниз по лестнице Задача для устного счёта: for var row := 1 to 9 do begin //Робот закрашивает клетки текущей горизонтали: for var col := 1 to row do begin Paint; Right; 196
end; //Робот возвращается к левой стене: for var col := 1 to row do Left; //Робот переходит на горизонталь ниже: Down; end; Задание cc8 Те же ступеньки, но «вверх ногами» (Рис. 1). Рис. 1. Держитесь крепче и цепче! 197
Самый простой алгоритм такой: Робот закрашивает клетки текущей горизонтали, а затем возвращается в её начало: for var row := 1 to 9 do begin //Робот клетки текущей горизонтали: for var col := 1 to 10-row do begin Paint; Right; end; //Робот возвращается к левой стене //и закрашивает клетки: for var col := 10-row downto 1 do Left; Здесь он спускается на следующую горизонталь и повторяет в ней свои действия: //Робот переходит на горизонталь ниже: Down; end; Чтобы не гонять Робота порожняком, можно после закраски нечётной горизонтали опускать его на чётную горизонталь, чтобы он закрасил её, идя в обратном направлении: for var row := 1 to 5 do begin //Робот клетки текущей горизонтали: for var col := 1 to 11 - row * 2 do begin Paint; Right; end; //Робот переходит на горизонталь ниже: Down; Left; //Робот возвращается к левой стене //и закрашивает клетки: 198
for var col := 10 - row * 2 downto 1 do begin Left; Paint; end; В самой нижней горизонтали это действие выполнять не нужно: //Робот переходит на горизонталь ниже: if (row < 5) then Down; end; Задание cc9 Робота ждёт своеобразный слалом с закрашиваем клеток (Рис. 1). Алгоритм легко составить, просто мысленно проведя Робота с первого уровня на второй. Затем все эти действия повторяются. Нужно только учесть, что длина коридора увеличивается на 1 клетку в каждой следующей горизонтали: for var row := 1 to 9 do begin //Робот закрашивает клетку в //текущей горизонтали: Paint; //идёт влево: for var col := 1 to row do Left; //переходит на горизонталь ниже: Down; //идёт вправо до стены: for var col := 1 to row do Right; end; 199
Рис. 1. Малярный слалом Задание cc10 Вариант Задания сс7, только теперь в каждой горизонтали вдвое больше точек (Рис. 1). Исходный код нужно исправить всего в трёх местах: for var row := 1 to 7 do begin //Робот закрашивает клетки текущей горизонтали: for var col := 1 to row*2 do 200
begin Paint; Right; end; //Робот возвращается к левой стене: for var col := 1 to row*2 do Left; //Робот переходит на горизонталь ниже: Down; end; Рис. 1. Крутые ступени 201
Задание cc11 Практически такое же задание, что и предыдущее, но с нечётным числом клеток в горизонталях (Рис. 1). Рис. 1. Невелика разница В данном случае горизонтали удобнее считать, начиная с нуля, тогда число клеток в горизонталях можно вычислить по простой формуле: row*2 + 1 202
Весь остальной код остался без изменений: for var row := 0 to 6 do begin //Робот закрашивает клетки текущей горизонтали: for var col := 1 to row*2 + 1 do begin Paint; Right; end; //Робот возвращается к левой стене: for var col := 1 to row*2 + 1 do Left; //Робот переходит на горизонталь ниже: Down; end; Задание cc12 Это задание продолжает развивать идеи нескольких предыдущих (Рис. 1). В этом задании опять нужно найти формулу для вычисления длины ряда клеток в зависимости от его номера. Нетрудно заметить, что длина ряда выражается степенью двойки, причём показатель степени равен номеру ряда, если считать с нуля: 2row В паскале степени вычисляются с помощью функции Power, которая возвращает результат вещественного типа, и нам необходимо приложить дополнительные усилия, чтобы привести его к целому типу. Других проблем при решении этого задания вы не встретите. 203
Рис. 1. Огромные ступени! for var row := 0 to 5 do begin //число закрашиваемых клеток в горизонтали: var n := System.Convert.ToInt32(Power(2, row)); //Робот закрашивает клетки текущей горизонтали: for var col := 1 to n do begin Paint; Right; end; //Робот возвращается к левой стене: for var col := 1 to n do Left; 204
//Робот переходит на горизонталь ниже: Down; end; Задание cc13 Слалом с изменяемой длиной горизонтальных отрезков (Рис.1). Рис. 1. Опять петляем 205
Первая горизонталь явно выбивается из общей закономерности, поэтому Робот сначала переходит в начало второй горизонтали, чтобы все остальные действия выполнялись циклически: //Робот переходит в начало второй горизонтали: for var col := 1 to 13 do Right; Down; //Робот возвращается к левой стене: for var col := 1 to 13 do Left; Теперь Робот должен 4 раза сделать вправо row * 2 шагов и закрасить клетку: for var row := 1 to 4 do begin //Робот идёт вправо: for var col := 1 to row * 2 do Right; //Робот закрашивает клетку: Paint; Потом вернуться в начало текущей горизонтали и сделать 2 шага вниз: //Робот возвращается к левой стене: if (row < 4) then for var col := 1 to row * 2 do Left; //Робот опускается ниже: Down; if (row < 4) then Down; end; Последние 2 горизонтали немного отличаются от прочих, поэтому действия Робота необходимо скорректировать. 206
Задание cc14 А вот теперь наш Робот попал в самый настоящий переплёт и лабиринт (Рис. 1). Рис. 1. Переплёт в лабиринте В этом задании опять выпадает первая горизонталь, поэтому Робот занимает исходную позицию в конце второй горизонтали: //Робот переходит в конец второй горизонтали: for var col := 1 to 13 do Right; Down; 207
Теперь Робот должен 4 раза выполнить сходные действия: идти влево, спуститься вниз, пойти вправо и снова сделать шаг вниз: for var row := 0 to 3 do begin //Робот идёт влево: for var col := 1 to 13 - row * 2 - 1 do Left; //Робот опускается ниже: Down; //Робот идёт вправо: if (row < 3) then begin for var col := 1 to 13 - row * 2 - 1 - 1 do Right; //Робот опускается ниже: Down; end; end; Как и в предыдущем задании, последняя итерация выполняется не полностью, что нужно учесть в алгоритме. Когда поле имеет постоянные размеры, мы можем посчитать длину каждого коридора на картинке, поэтому вполне естественно для перемещения Робота использовать циклы с параметром. Однако более универсальный цикл while правильно работает при любой длине коридоров, и алгоритм получается более простым и понятным: //число коридоров: var n := 8; //Робот идёт по всем коридорам: while (n > 0) do begin //Робот идёт вправо до стены: while (FreeFromRight) do Right; //Робот опускается вниз: 208
Down; n -= 1; //Робот идёт влево до стены: while (FreeFromLeft) do Left; //Робот опускается вниз: Down; n -= 1; end; Задание cc15 Робот получил ответственное задание: закрасить равнобедренный треугольник (Рис. 1). Рис. 1. Треугольные дела 209
Самый экономичный путь по части ходьбы: сновать вправо – влево, учитывая прирост длины закрашиваемых клеток по мере приближения к основанию треугольника: for var row := 0 to 3 do begin //Робот идёт вправо и закрашивает клетки: for var col := 1 to row * 4 + 1 do begin Paint; Right; end; //Робот опускается ниже: Down; Последняя пара горизонталей отличается от прочих, поэтому её нужно проходить другим способом: //Робот идёт влево и закрашивает клетки: if (row < 3) then begin for var col := 1 to row * 4 + 2 do begin Paint; Left; end; Paint; Down; Left; end //нижняя горизонталь: else for var col := 1 to 7 do Left; end; Можно заставить Робота выполнять другой алгоритм. Действительно, он может закрашивать не горизонтальные ряды, а стороны вложенных треугольников. Этот способ не менее забавный, чем первый: 210
for var n := 0 to 3 do begin //длина боковых сторон треугольника: var size := 6 - n * 2; //Робот закрашивает клетку на вершине треугольника: Paint; //Робот закрашивает левую боковую сторону: for var sz := 1 to size do begin Down; Left; Paint; end; //Робот закрашивает основание: for var sz := 1 to size + 6 - n * 2 do begin Right; Paint; end; //Робот закрашивает правую боковую сторону: for var sz := 1 to size do begin Up; Left; Paint; end; //Робот переходит на следующий треугольник: Down; end; //Робот идёт на базу: for var row := 1 to 3 do Down; 211
Задание cc16 Робот должен закрасить зубатую пилу (Рис. 1). Рис. 1. Попилили? Мы без труда сосчитаем зубья пилы – их всего четыре: //Робот закрашивает 4 треугольника: for var n := 1 to 4 do begin 212
Треугольные зубья можно закрашивать по-разному, но мы должны учитывать, что в конце выполнения задания Робот должен оказаться в начале следующего, пятого треугольника. Поэтому Робот может закрашивать вертикальные ряды, поднимаясь вверх, а затем спускаться до основания: //в каждом 5 колонок: for var col := 1 to 5 do begin //Робот закрашивает колонку, //шагая вверх: for var m := 1 to col do begin Paint; Up; end; //Робот опускается до основания треугольника: for var m := 1 to col do Down; //Робот переходит к следующей колонке: Right; end; end; Задание непростое, поэтому его можно решить далеко не единственным способом! 213
Задание cc17 Более сложная пила, чем в предыдущем задании (Рис. 1). Рис. 1. Суперпила Число зубцов увеличилось до 6, а их размеры убывают справа налево, поэтому во внешнем цикле удобнее использовать цикл с downto: //Робот закрашивает 6 треугольников: for var n := 6 downto 1 do begin //в каждом n колонок: for var col := 1 to n do 214
begin //Робот закрашивает колонку, //шагая вверх: for var m := 1 to col do begin Paint; Up; end; //Робот опускается до основания треугольника: for var m := 1 to col do Down; //Робот переходит к следующей колонке: Right; end; Так как базовая клетка находится в непосредственной близости к последнему треугольнику, то дополнительный шаг вправо Роботу делать не нужно: //Робот переходит к следующему треугольнику: if (n > 1) then Right; end; Задание cc18 Коридорное воспитание Робота продолжается (Рис. 1)! В условии задания явно указано, что мы должны использовать цикл while. Это избавит нас от необходимости измерять длину каждого коридора. Алгоритм получился простой и понятный: //всего Робот должен пройти 7 коридоров: for var n := 1 to 7 do begin 215
//Робот идёт до правой стены и //закрашивает клетку: while (FreeFromRight) do Right; Paint; //Робот возвращается в левой стене: while (FreeFromLeft) do Left; Рис. 1. Коридоры, коридоры… Единственная «изюмина» задания, которую мы должны учесть, - закончив беготню по нижнему коридору, Робот не делает шаг вниз – туда ему пути нет: 216
//Робот шагает вниз: if (n < 7) then Down; end; Задание cc19 Наконец-то! Изменчивое задание (Рис. 1). Рис. 1. Что-то изменилось? 217
Дойти до правой стенки, закрасить там клетку и вернуться обратно – это Робот сделает без труда. Вопрос в том, когда он должен остановиться. Ответ легко найти, если внимательно посмотреть на Рис. 1⬆: Робот переходит вниз, пока под ним не появляется стена: while (true) do begin //Робот идёт до правой стены и //закрашивает клетку: while (FreeFromRight) do Right; Paint; //Робот возвращается в левой стене: while (FreeFromLeft) do Left; //Робот шагает вниз, //пока под ним нет стены: if (WallFromDown) then break; Down; end; 218
Набор заданий mix Программистское тутти-фрутти на 10 заданий (Рис. 1). Рис. 1. Всякая всячина Задание mix1 Наши любимые задания на изменяемые поля (Рис. 1)! Размеры закрашиваемого прямоугольника заранее неизвестны, но стартовая клетка всегда находится в его левом верхнем углу, а финишная – в правом нижнем (Рис. 2). Робот быстрее закрасит прямоугольник, если будет закрашивать клетки, шагая и влево, и вправо. Но если вправо он может идти до стены, то влево ему нужно пройти столько же шагав, сколько и вправо, поэтому Робот должен считать шаги. 219
Рис. 1. Зыбкий прямоугольник Рис. 2. Разноразмерные прямоугольники 220
Робот закрашивает прямоугольник парами горизонтальных строк. Так как он не знает высоты прямоугольника, то работает без устали, то есть бесконечно. Однако для нас важно, чтобы он остановился, когда окажется в закрашенной базовой клетке. А её легко определить, когда Робот закрасит очередную горизонталь, идя вправо. Если он окажется в правом нижнем углу, то задание прерывается: //Робот закрашивает прямоугольник: while (true) do begin var width := 0; //Робот идёт вправо: while (FreeFromRight) do begin Paint; Right; width += 1; end; Paint; //Робот лошёл до базы: if (WallFromDown and WallFromRight) then break; //Робот делает шаг вниз: if (FreeFromDown) then Down; //и закрашивает клетки в обратном направлении: for var w := 1 to width do begin Paint; Left; end; Paint; //Робот делает шаг вниз: if (FreeFromDown) then Down; end; 221
Задание mix2 Усложнённый вариант первого задания. Теперь Робот должен закрашивать клетки в шахматном порядке (Рис. 1). Рис. 1. Шах - не мат Опять быстрее закрашивать пары горизонталей, шагая вправо, а затем влево. Шаги следует считать в обоих направлениях, чтобы правильно закрашивать клетки: //Робот закрашивает клетки //прямоугольника в шахматном порядке: 222
while (true) do begin //число шагов влево и вправо: var step := 1; //Робот идёт вправо: Paint; while (FreeFromRight) do begin Right; step += 1; if (step mod 2 = 1) then Paint; end; Если Робот оказался в первой нижней клетке, идя вправо, то задание он уже выполнил: //Робот лошёл до базы: if (WallFromDown and WallFromRight) then break; //Робот делает шаг вниз: if (FreeFromDown) then Down; var back := step + 1; //и закрашивает клетки в обратном направлении: while (back < step * 2) do begin if (back mod 2 = 1) then Paint; back += 1; Left; end; Если Робот закрасил последнюю горизонталь, идя влево, то ему ещё предстоит вернуться на базу: //Робот делает шаг вниз: 223
if (FreeFromDown) then Down //Робот в нижней горизонтали //и должен вернуться на базу: else begin while (FreeFromRight) do Right; break; end; end; Задание mix3 Роботу опять предстоит закрасить квадрат, но прежде он должен измерить его (Рис. 1). Рис. 1. Измеряем квадраты 224
Длину стороны квадрата можно найти так. Робот идёт по диагонали вниз-влево и считает шаги: //размеры квадрата: var size := 1; //Робот измеряет квадрат: while (CellIsFree) do begin size += 1; Down; Left; end; В переменной size теперь хранится длина сторон квадрата, и Робот может закрасить столько же клеток, идя вправо. Чтобы не гонять Робота лишний раз, мы начнём работу с левого нижнего угла, в котором он оказался после измерения квадрата: //счётчик шагов: var n := 0; while (true) do begin //Робот идёт вправо: for var col := 1 to size do begin Paint; if (col < size) then Right; end; Чтобы Робот не сделал лишний шаг вправо, мы дополнительно контролируем его шаги! Увеличиваем счётчик закрашенных горизонталей: //Робот на базе: n += 1; 225
Если Робот находится в верхней горизонтали, идя вправо, то он уже на базе: if (n = size) then break; Иначе он поднимается в следующую горизонталь и закрашивает её, идя влево: //Робот делает шаг вверх: Up; //Робот идёт влево: for var col := 1 to size do begin Paint; if (col < size) then Left; end; Если Робот находится в верхней горизонтали, которую он уже закрасил до начала, то он должен вернуться на базу, шагая вправо: n += 1; if (n = size) then begin //Робот идёт на базу: for var col := 1 to size-1 do Right; break; end; Если Робот не в верхней горизонтали, то он опять поднимается вверх: //Робот делает шаг вверх: Up; end; 226
Задание mix4 Намечаются поисково-разыскные работы (Рис. 1)! Рис. 1. Ищем! Закрашенная клетка может находиться в любом месте поля, поэтому поиски следует вести систематически! Для этого Робот сначала переходит в верхнюю левую клетку: //Робот переходит в верхнюю левую клетку: while(FreeFromUp) do 227
Up; while(FreeFromLeft) do Left; А затем спускается зигзагами вниз. Сначала он проверяет, не стоит ли уже на закрашенной клетке. Если это так, то задание выполнено. Иначе он идёт вправо до стены и после каждого шага проверяет это условие: //Робот "сканирует" поле слева направо //и сверху вниз: while(true) do begin //Робот идёт вправо: while (FreeFromRight) do begin //нашёл закрашенную клетку: if (CellIsPainted) then exit; Right; end; //если внизу нет стены, //то Робот делает шаг вниз: if (FreeFromDown) then Down; //те же самые действия, но влево: while(FreeFromLeft) do begin if (CellIsPainted) then exit; Left; end; if (FreeFromDown) then Down; end; Понятно, что, пропахав всё поле вдоль и поперёк, Робот непременно найдёт закрашенную клетку! 228
Задание mix5 Поиск закрашенных клеток продолжается (Рис. 1)! Рис. 1. Танцуем от закрашенной клетки Первая часть задачи – поиск закрашенной клетки – Роботу уже знакома по предыдущему заданию: //Робот переходит в верхнюю левую клетку: while(FreeFromUp) do Up; 229
while(FreeFromLeft) do Left; Но теперь нужно учесть, что на этом труды Робота не заканчиваются, поэтому мы не можем использовать оператор exit, прерывающий выполнение программы. Мы объявляем флаг found, который будет сигнализировать о «поимке» закрашенной клетки. Он необходим потому, что оператор break прервёт только вложенный цикл while, и мы затем должны ещё выйти из внешнего цикла. В таких случаях допускается применение оператора безусловного перехода goto, но в данном случае без него легко обойтись: var found := false; //Робот "сканирует" поле слева направо //и сверху вниз: while(true) do begin //Робот идёт вправо: while (FreeFromRight) do begin //нашёл закрашенную клетку: if (CellIsPainted) then begin found := true; break; end; Right; end; if (found) then break; //если внизу нет стены, //то Робот делает шаг вниз: if (FreeFromDown) then Down; //те же самые действия, но влево: while(FreeFromLeft) do begin if (CellIsPainted) then begin 230
found := true; break; end; Left; end; if (found) then break; if (FreeFromDown) then Down; end; Первая часть задачи выполнена – //Робот стоит на закрашенной клетке Чтобы закрасить вертикальный ряд клеток, Робот поднимается вверх, а затем идёт вниз до стены и закрашивает клетки. Всё было так просто, если бы Роботу не нужно было бы после этого возвращаться на базу. Так как теперь все клетки в его вертикали закрашены, то он не сможет распознать базовую клетку, поэтому нужен дополнительный счётчик, чтобы запомнить в нём нужную горизонталь: //счётчик горизонталей: var row := 0; //Робот идёт вверх до стены: while(FreeFromUp) do begin Up; row -= 1; end; //Робот идёт вниз и закрашивает клетки: while(FreeFromDown) do begin Paint; Down; row += 1; end; Paint; 231
Все требуемые клетки закрашены, а в переменной row хранится число шагов вверх, которые должен сделать Робот: //Робот возвращается на базу: for var r := 1 to row do Up; Задание mix6 Симметричные покрасочные работы. Робот закрашивает клетки справа, у которых имеется закрашенная пара слева (Рис. 1). Рис. 1. Симметрично и симпатично 232
Алгоритм достаточно простой. Если Робот стоит на закрашенной клетке у левой стены, то он идёт вправо до упора, закрашивает там клетку и возвращается назад. Пока снизу нет стены, Робот спускается на следующую горизонталь: //Робот идёт вниз до стены: while (FreeFromDown) do begin //если Робот стоит на закрашенной клетке: if (CellIsPainted) then begin //Робот идёт вправо до стены: while (FreeFromRight) do Right; //и закрашивает клетку: Paint; //Робот возвращается к левой стене: while (FreeFromLeft) do Left; end; //Робот делает шаг вниз: Down; end; Задание mix7 Приключения продолжаются! Робот должен закрасить клетки во всех горизонтальных рядах, в которых имеются 2 закрашенные клетки (Рис. 1). Задание несложное. 233
Если Робот стоит слева на закрашенной клетке, то он идёт до правой стены, чтобы узнать, есть ли в этой горизонтали вторая закрашенная клетка. Рис. 1. Попарно Если такой клетки он не находит, то просто возвращается назад. В противном случае он ещё и закрашивает клетки: //Робот идёт вниз до стены: while (FreeFromDown) do begin //если Робот стоит на закрашенной клетке: if (CellIsPainted) then begin //Робот идёт вправо до стены: 234
while (FreeFromRight) do Right; Чтобы Робот мог правильно выполнить свои действия при возвращении к левой стене, мы запоминаем в переменной painted, нашёл ли он справа закрашенную клетку: //если там закрашенная клетка: var painted := CellIsPainted; //Робот возвращается к левой стене //и по пути закрашивает клетки: while (FreeFromLeft) do begin Left; if (painted) then Paint; end; end; //Робот делает шаг вниз: Down; end; Задание mix8 Приключения Робота продолжаются, а задания усугубляются! Теперь Робот должен ещё и считать закрашенные клетки по ходу своего движения (Рис. 1). Алгоритм практически тот же самый, что и в предыдущем задании. Нужно только подправить условие закрашивание клеток горизонтального ряда. А оно простое: в нём должны быть закрашены ровно 3 клетки, из которых две – крайние слева и справа. 235
Рис. 1. Сочтёмся! //Робот идёт вниз до стены: while (FreeFromDown) do begin //если Робот стоит на закрашенной клетке: if (CellIsPainted) then begin //число закрашенных клеток: var nPainted := 1; //Робот идёт вправо до стены: while (FreeFromRight) do begin Right; if (CellIsPainted) then nPainted += 1; end; 236
//общее число закрашенных клеток //должно равняться 3, а последняя клетка //горизонтали также должна быть закрашена: var painted := (nPainted = 3) and CellIsPainted; //Робот возвращается к левой стене //и по пути закрашивает клетки: while (FreeFromLeft) do begin Left; if (painted) then Paint; end; end; //Робот делает шаг вниз: Down; end; Задание mix9 Задание напоминает mix3, но теперь Робот должен закрасить не квадрат, а прямоугольник (Рис. 1). Многосерийное задание, алгоритм которого можно построить, как из кирпичиков, из алгоритмов наших предыдущих заданий. Чтобы найти закрашенную клетку, Роботу всё равно нужно дойти до верхнего левого угла поля. При этом он может определить и координаты начальной/базовой клетки, которые потом понадобятся ему при вычислении размеров прямоугольника: //координаты базы (0..): var colBase := 0; var rowBase := 0; 237
//Робот идёт влево: while (FreeFromLeft) do begin colBase += 1; Left; end; //writeln(colBase); //Робот идёт вверх: while (FreeFromUp) do begin rowBase += 1; Up; end; //writeln(rowBase); Рис. 1. Прямоугольник сложнее квадрата 238
Сейчас Робот находится в левом верхнем углу и знает координаты базовой клетки. Теперь он ищет на поле закрашенную клетку и заодно определяет её координаты: //координаты закрашенной клетки (0..): var colPainted := 0; var rowPainted := 0; var found := false; //Робот "сканирует" поле слева направо //и сверху вниз: while(true) do begin //Робот идёт вправо: while (FreeFromRight) do begin //нашёл закрашенную клетку: if (CellIsPainted) then begin found := true; break; end; Right; colPainted += 1; end; if (found) then break; //если внизу нет стены, //то Робот делает шаг вниз: if (FreeFromDown) then begin Down; rowPainted += 1; end; //те же самые действия, но влево: while(FreeFromLeft) do begin if (CellIsPainted) then begin found := true; 239
break; end; Left; colPainted -= 1; end; if (found) then break; if (FreeFromDown) then begin Down; rowPainted += 1; end; end; //Робот стоит на закрашенной клетке //writeln(colPainted); //writeln(rowPainted); Робот стоит на закрашенной клетке и знает её координаты. Размеры сторон прямоугольника легко вычислить как разность координат базовой и закрашенной клеток: //размеры прямоугольника: var width := colBase - colPainted + 1; var height := rowPainted - rowBase + 1; Закрашивать квадрат Робот уже умеет. Прямоугольник закрашивается почти так же, но с учётом того, что размеры сторон могут различаться: //счётчик шагов: var n := 0; while (true) do begin //Робот идёт вправо: for var col := 1 to width do begin Paint; if (col < width) then 240
Right; end; //Робот на базе: n += 1; if (n = height) then break; //Робот делает шаг вверх: Up; //Робот идёт влево: for var col := 1 to width do begin Paint; if (col < width) then Left; end; n += 1; if (n = height) then begin //Робот идёт на базу: for var col := 1 to width-1 do Right; break; end; //Робот делает шаг вверх: Up; end; Задание mix10 Задание, очень похожее на предыдущее, но с усложнениями (Рис. 1). Базовая и закрашенная клетки находятся в противоположных углах прямоугольника, но разными способами (Рис. 2). 241
Рис. 1. Будут осложнения! Рис. 2. Разные противоположности 242
Размеры прямоугольника Робот может найти так же, как в предыдущем задании, то нужно взять абсолютную величину разности координат: //координаты базы (0..): var colBase := 0; var rowBase := 0; //Робот идёт влево: while (FreeFromLeft) do begin colBase += 1; Left; end; //Робот идёт вверх: while (FreeFromUp) do begin rowBase += 1; Up; end; //координаты закрашенной клетки (0..): var colPainted := 0; var rowPainted := 0; var found := false; //Робот "сканирует" поле слева направо //и сверху вниз: while(true) do begin //Робот идёт вправо: while (FreeFromRight) do begin //нашёл закрашенную клетку: if (CellIsPainted) then begin found := true; break; end; Right; colPainted += 1; end; if (found) then break; 243
//если внизу нет стены, //то Робот делает шаг вниз: if (FreeFromDown) then begin Down; rowPainted += 1; end; //те же самые действия, но влево: while(FreeFromLeft) do begin if (CellIsPainted) then begin found := true; break; end; Left; colPainted -= 1; end; if (found) then break; if (FreeFromDown) then begin Down; rowPainted += 1; end; end; //Робот стоит на закрашенной клетке //размеры прямоугольника: var width := Abs(colBase – colPainted) + 1; var height := Abs(rowPainted – rowBase) + 1; Так как мы умеем закрашивать прямоугольник, начиная с левого нижнего угла, то перегоняем Робота туда: //Робот идёт в левый верхний угол поля: while (FreeFromLeft) do Left; while (FreeFromUp) do Up; 244
//Робот идёт в нижний левый угол прямоугольника: for var col := 1 to Min(colBase, colPainted) do Right; for var row := 1 to Max(rowBase, rowPainted) do Down; С этой частью работы Робот справляется без проблем: //Робот закрашивает прямоугольник //счётчик шагов: var n := 0; while (true) do begin //Робот идёт вправо: for var col := 1 to width do begin Paint; if (col < width) then Right; end; n += 1; if (n = height) then break; //Робот делает шаг вверх: Up; //Робот идёт влево: for var col := 1 to width do begin Paint; if (col < width) then Left; end; n += 1; if (n = height) then break; //Робот делает шаг вверх: Up; end; 245
И наконец, Робот должен вернуться на базу. Её координаты мы знаем, поэтому действуем так. Перегоняем Робота в верхний левый угол: //Робот идёт в левый верхний угол поля: while (FreeFromLeft) do Left; while (FreeFromUp) do Up; А затем отправляем его точно по адресу: //Робот идёт на базу: for var col := 1 to colBase do Right; for var row := 1 to rowBase do Down; Алгоритм получился не оптимальный, но зато надёжный и простой! 246
Набор заданий p В программировании принято выделять повторяющиеся законченные части кода в отдельные подпрограммы. В данном наборе заданий это процедуры без параметров (Рис. 1). Рис. 1. Пора на процедуры Процедуры могут использоваться в разных программах. Это очень удобно, поскольку они уже написаны и отлажены. Таким образом, процедуры можно рассматривать как составные части, или детали какого-то сложного механизма. Все без исключения серьёзные программы состоят из большого числа процедур. Задание p1 В первом задании Роботу поручено важное задание: закрасить на поле 3 одинаковых креста (Рис. 1). Понятно, что одинаковые фигуры и закрашиваются одинаково, то есть Робот должен выполнить одну и ту же последовательность действий. Если он сумеет закрасить один крест, то, действуя так же, сможет закрасить их сколько угодно. 247
Обращаем внимание на то, что базовая клетка находится в центре креста, поэтому в ней он и должен заканчивать выполнение процедуры. Рис. 1. Или грудь в крестах… Последовательность закрашивания клеток может быть разной. Например, Робот может закрасить центральную клетку, затем подняться вверх и закрасить внешние клетки, обходя их по часовой стрелке или в обратном направлении: procedure Cross(); begin //закрашиваем центральную клетку: Paint; //закрашиваем остальные клетки: 248
Left; Paint; Up; Right; Paint; Down; Right; Paint; Down; Left; Paint; //возвращаемся в центральную клетку: Up; end; Не забудьте вернуть Робота в центральную клетку! В главной части программы мы сначала вызываем процедуру Cross, а потом последовательно перегоняем Робота в центр остальных крестов и опять вызываем эту процедуру: begin Task('p1'); //закрашиваем первый крест: Cross; //идём ко второму кресту: Up; Right; Right; Right;Right; Right; //закрашиваем второй крест: Cross; //идём к третьему кресту: Up;Up;Up; Left; Left; Left; //закрашиваем третий крест: Cross; end. Задание p2 Задание, подобное предыдущему, но менее мрачное – Роботу нужно закрасить небольшие квадраты (Рис. 1). 249
Рис. 1. Квадратно-гнездовой закрас Опять обращаем внимание, что Робот заканчивает выполнение программы в верхней левой клетке закрашиваемого квадрата, поэтому в процедуре Square2 возвращаем его на место. Закрашивать квадрат можно в любую сторону: procedure Square2(); begin Paint; Right; Paint; Down; Paint; Left; Paint; //возвращаемся в исходную клетку: 250
Up; end; В этом задании квадраты располагаются регулярно, поэтому переходить от одного квадрата к другому можно в цикле for: begin Task('p2'); //закрашиваем 6 квадратов: for var n := 1 to 6 do begin Square2; if (n < 6) then begin Right; Right;Right; end; end; end. Задание p3 Те же самые квадраты, но расположенные по диагонали (Рис. 1). Процедура Square2 осталась без изменений (бережём силы и время!), а в главной части программы нужно исправить только команды, отвечающие за переход к следующему квадрату: begin Task('p3'); //закрашиваем 6 квадратов: for var n := 1 to 6 do begin Square2; if (n < 6) then begin 251
Left; Left; Up; Up; end; end; end. Рис. 1. Наискосок Задание p4 А здесь маленькие квадраты сбились в плотную кучку, образовав БОЛЬШОЙ квадрат (Рис. 1). 252
Рис. 1. Скучковались В очередной раз пользуемся плодами своего труда и просто копируем процедуру Square2 из нашего предыдущего проекта: Новая процедура Square4 вызывает процедуру Square2 для рисования «элементарных» квадратов и выполняет необходимые переходы между ними: procedure Square4(); begin Square2(); Right; Right; Square2(); 253
Down; Down; Square2(); Left; Left; Square2(); Up; Up; end; На долю основной программы достался только вызов новой процедуры: begin Task('p4'); Square4(); end. Вот так, за счёт процедур и сокращают основную программу! Задание p5 Размеры закрашиваемого квадрата ещё увеличились (Рис. 1)! Теперь процедура Square4 уже сама оказалась в роли более «мелкой» процеду- ры Square2. Так как новую процедуру нам писать не нужно, то все вызовы процедуры Square4 и переходы между квадратами 4 х 4 клетки мы выполняем в главной части программы: begin Task('p5'); Square4(); Right; Right; Right; Right; Square4; 254
Down; Down; Down; Down; Square4; Left; Left; Left; Left; Square4; Up; Up; Up; Up; end. Рис. 1. Увесистый квадрат! 255
Задание p6 На этом квадраты заканчиваются – пришло время горизонтальных рядов (Рис. 1). Рис. 1. По горизонтали Процедуру Row для закрашивания клеток горизонтального ряда можно написать несколькими способами. Самый универсальный и простой – отогнать Робота к правой стенке, а затем вернуть его к левой стенке, понуждая по ходу движения закрашивать клетки: procedure Row(); begin //Робот идёт вправо до стены: while(FreeFromRight) do Right; 256
//Робот идёт влево до стены //и закрашивает клетки: while(FreeFromLeft) do begin Paint; Left; end; Paint; end; Так как ряды расположены регулярно, то опять пользуемся циклом for для перехода к следующим рядам: begin Task('p6'); //Робот закрашивает 4 ряда: for var n := 1 to 4 do begin Row(); if (n < 4) then begin Down; Down; end; end; end. 257
Задание p7 Горизонтальные ряды сменились вертикальными – вот и вся разница (Рис. 1)! Рис. 1. Закраска по вертикали Процедуру Column нетрудно переделать из процедуры Row: procedure Column(); begin //Робот идёт вниз до стены: while(FreeFromDown) do Down; //Робот идёт вверх до стены //и закрашивает клетки: while(FreeFromUp) do begin 258
Paint; Up; end; Paint; end; То же самое относится и к главной части программы: begin Task('p7'); //Робот закрашивает 4 ряда: for var n := 1 to 4 do begin Column(); if (n < 4) then begin Right; Right; end; end; end. 259
Задание p8 А в этом задании Роботу нужно закрасить и горизонтальные, и вертикальные ряды (Рис. 1). Рис. 1. И те, и другие Процедуры Row и Column уже написаны – слава процедурам! – поэтому мы сразу берёмся за код главной части программы, опять же копируя большую его часть из предыдущих проектов – слава нашему труду! begin Task('p8'); 260
//Робот закрашивает 4 горизонтальных ряда: for var n := 1 to 4 do begin Row(); if (n < 4) then begin Down; Down; end; end; //Робот переходит к вертикальным рядам: while (FreeFromUp) do Up; Right; //Робот закрашивает 4 вертикальных ряда: for var n := 1 to 4 do begin Column(); if (n < 4) then begin Right; Right; end; end; end. 261
Задание p9 Робот продолжает своё победное шествие с процедурами Row и Column (Рис. 1). Рис. 1. Уголки Правда, на этот раз обе процедуры необходимо поправить, потому что Робот идёт не до стены, а делает вполне определённое число шагов: procedure Row(); begin //Робот идёт вправо: for var r:= 1 to 8 do 262
Right; //Робот идёт влево //и закрашивает клетки: for var r:= 1 to 8 do begin Paint; Left; end; Paint; end; procedure Column(); begin //Робот идёт вниз: for var r:= 1 to 8 do Down; //Робот идёт вверх //и закрашивает клетки: for var r:= 1 to 8 do begin Paint; Up; end; Paint; end; Стороны уголков можно закрашивать в любом порядке: begin Task('p9'); //Робот закрашивает 3 горизонтальных и //вертикальных ряда: for var n := 1 to 3 do begin Row(); Column(); if (n < 3) then begin Down; Down; 263
Right; Right; end; end; end. Задание p10 Ура! Пишем новую процедуру – для обхода квадрата по контуру (Рис. 1). Рис. 1. Квадратный контур Процедура, конечно, самая незамысловатая: 264
procedure Contour(); begin //верхняя сторона: for var n := 1 to 4 do begin Paint; Right; end; //правая сторона: for var n := 1 to 4 do begin Paint; Down; end; //нижняя сторона: for var n := 1 to 4 do begin Paint; Left; end; //левая сторона: for var n := 1 to 4 do begin Paint; Up; end; end; Да и главная часть программы не сложнее: begin Task('p10'); //Робот закрашивает 5 квадратов: for var k := 1 to 5 do begin 265
Contour(); //Робот переходит к следующему квадрату: if (k < 5) then for var n := 1 to 6 do Right; end; end. 266
Набор заданий pp Последний набор заданий также отдан процедурам, но на этот раз с параметрами (Рис. 1). Рис. 1. Нас ждут параметры! Процедуры без параметров менее универсальны. Например, все рассмотренные нами выше процедуры не смогли бы нарисовать крестик или квадратик другого размера. А вот процедуры с параметрами могут гораздо больше, поэтому они интереснее и полезнее! Задание pp1 Элементарное задание на закрашивание одной клетки в каждой горизонтали (Рис.1). Процедуры очень простые. Главное – не забыть, что, идя вправо, Робот закрашивает клетку, а возвращаясь влево, не закрашивает: //РОБОТ ИДЁТ ВПРАВО n ШАГОВ //И ЗАКРАШИВАЕТ КЛЕТКУ procedure RightN(n: integer); 267
begin for var i := 1 to n do Right; Paint; end; //РОБОТ ИДЁТ ВЛЕВО n ШАГОВ procedure LeftN(n: integer); begin for var i := 1 to n do Left; end; Рис. 1. По одной 268
В главной части программы мы вызываем процедуры с соответствующими аргументами и аккуратно переводим Робота вниз после возвращения: // ГЛАВНАЯ ПРОГРАММА begin Task('pp1'); RightN(6); LeftN(6); Down; RightN(8); LeftN(8); Down; RightN(4); LeftN(4); Down; RightN(3); LeftN(3); Down; RightN(10); LeftN(10); Down; RightN(1); LeftN(1); Down; RightN(6); LeftN(6); end. Обратите внимание, что, находясь в нижней горизонтали, Робот вниз не идёт! Задание pp2 Роботу поручено закрасить вертикальные ряды клеток переменной длины (Рис. 1). Ну а нам поручено написать процедуру PaintDown для нашего милого Робота. Так как базовая клетка находится у верхней стены, то после закраски вертикального ряда мы должны вернуть Робота на место: procedure PaintDown(n: integer); begin //Робот идёт вниз и закрашивает клетки: for var r := 1 to n do begin Paint; Down; 269
end; //Робот возвращается наверх: for var r := 1 to n do Up; end; Рис. 1. Вертикальные работы Это приём облегчит нам и переход к следующим вертикальным рядам: begin Task('pp2'); 270
PaintDown(7); Right; Right; PaintDown(4); Right; Right; PaintDown(9); Right; Right; PaintDown(6); Right; Right; PaintDown(8); end. Задание pp3 Спиральный штопор – задание на 4 процедуры (Рис. 1)! Рис. 1. Вошли в штопор 271
Элементарные процедуры и написать элементарно просто: procedure RightN(n: integer); begin //Робот идёт вправо: for var i := 1 to n do Right; end; procedure LeftN(n: integer); begin //Робот идёт влево: for var i := 1 to n do Left; end; procedure DownN(n: integer); begin //Робот идёт вниз: for var i := 1 to n do Down; end; procedure UpN(n: integer); begin //Робот идёт вверх: for var i := 1 to n do Up; end; А в главной части программы нужно только аккуратно подсчитывать клетки в каждом витке спирали и записывать в вызовы соответствующих процедур: begin Task('pp3'); //Робот идёт по спирали: DownN(8); RightN(8); UpN(8); LeftN(7); 272
DownN(7); RightN(6); UpN(6); LeftN(5); DownN(5); RightN(4); UpN(4); LeftN(3); DownN(3); RightN(2); UpN(2); LeftN(1); DownN(1); end. Кстати говоря, в длине витков спирали имеется определённая закономерность, так что код главной части программы можно существенно сократить за счёт использования цикла for. Задание pp4 «Символическое» задание на закрашивание буквы Т различного написания (Рис. 1). По условию задачи, мы должны снабдить Робота процедурой SymbolT с двумя параметрами. Параметр m обозначает половину ширины буквы. Однако все буквы имеют нечётную ширину, поэтому под половиной мы будем понимать меньшую половину. Что касается параметра h, то про него ничего не говорится, но понятно, что это высота ножки буквы Т. Мы будем считать, что это полная высота. Тогда процедура SymbolT может быть такой. 273
Рис. 1. Три тэ procedure SymbolT(m, h: integer); begin //Робот идёт влево: for var i := 1 to m do Left; //Робот идёт вправо и закрашивает клетки: for var i := 1 to m * 2 + 1 do begin Paint; Right; end; 274
//Робот возвращается в начальную клетку: for var i := 1 to m+1 do Left; //Робот идёт вниз: for var i := 1 to h-1 do Down; //Робот идёт вверх и закрашивает клетки: for var i := 1 to h-1 do begin Paint; Up; end; end; Много лишней беготни, но зато всё просто и понятно! В главной части программы мы вызываем нашу процедуру с соответствующими значениями параметров и переходим к следующей букве: begin Task('pp4'); //первый символ: SymbolT(3, 4); //переход: for var i := 1 to 7 do Right; SymbolT(2, 5); //переход: for var i := 1 to 5 do Right; SymbolT(1, 6); end. 275
Задание pp5 Прямоугольник мы уже закрашивали неоднократно, но без процедур (Рис. 1). Рис. 1. Прямо угольник! Процедура Rect получилась довольно простая, потому что высота прямоугольника выражается чётным числом. Это значит, что Робот может закрашивать по 2 ряда за итерацию: сначала шагая вправо, а затем влево: procedure Rect(w, h: integer); begin for var r := 1 to h div 2 do begin 276
//Робот идёт вправо и закрашивает клетки: for var c := 1 to w do begin Paint; Right; end; Left; //Робот делает шаг вниз: Down; //Робот идёт влево и закрашивает клетки: for var c := 1 to w do begin Paint; Left; end; Right; //Робот делает шаг вниз: Down; end; После трудов праведных Робота следует вернуть на базу для заслуженного вознаграждения: //Робот возвращается на базу: for var r := 1 to h do Up; end; Задание pp6 Робот должен закрасить 5 прямоугольников различных размеров (Рис. 1). Так как нас никто не предупредил, что процедура Rect понадобится и в другом задании, то мы написали слишком простую процедуру, которая не работает с прямоугольниками нечётной высоты. 277
Рис. 1. Их стало больше! Чтобы не писать процедуру заново, слегка усовершенствуем старую, изменив и добавив несколько строк кода: procedure Rect(w, h: integer); begin //число итераций: var h2 := (h + 1) div 2; for var r := 1 to h2 do begin //Робот идёт вправо и закрашивает клетки: for var c := 1 to w do begin 278
Paint; Right; end; Left; //если высота прямогольника чётная //или итерация не последняя: if ((h mod 2 = 0) or (r < h2)) then //Робот делает шаг вниз: Down; //Робот идёт влево и закрашивает клетки: for var c := 1 to w do begin Paint; Left; end; Right; //Робот делает шаг вниз: Down; end; //Робот возвращается на базу: for var r := 1 to h do Up; end; Попробуйте оптимизировать эту процедуру! В главной части программы, как обычно, мы вызываем процедуру с нужными значениями параметров и пересылаем Робота от одного прямоугольника к другому: begin Task('pp6'); Rect(5, 3); 279
for var i := 1 to 7 do Right; Up; Rect(2, 6); Right; Right; Right; Rect(4, 6); for var i := 1 to 7 do Down; Left; Left; Rect(8, 3); for var i := 1 to 7 do Left; Up;Up; Rect(5, 5); end. Процедура закрашивания прямоугольника без лишних шагов Робота не столь проста, как хотелось бы: procedure RectEx(w, h: integer); begin //число итераций всегда чётное: var h2 := h + h mod 2; for var r := 1 to h2 do begin //если итерация нечётная, //Робот идёт вправо и закрашивает клетки: if (r mod 2 = 1) then begin Paint; for var c := 1 to w - 1 do begin Right; Paint; end; if (r < h) then Down; end //если итерация чётная, 280
//Робот идёт влево и закрашивает клетки: else begin Paint; for var c := 1 to w - 1 do begin Left; if (r <= h) then Paint; end; //Робот делает шаг вниз: if (r < h2) then Down; end; end; //Робот возвращается на базу: for var r := 1 to h - 1 do Up; end; Задание pp7 Робот ходит по кругу, который в этом задании называется периметром прямоугольника (Рис. 1). Если вы помните процедуру Contour, то просто скопируйте её в проект и подправьте диапазоны изменения параметров циклов: procedure Perimeter(w, h: integer); begin //верхняя сторона: for var n := 1 to w-1 do begin Paint; Right; end; //правая сторона: for var n := 1 to h-1 do begin 281
Paint; Down; end; //нижняя сторона: for var n := 1 to w-1 do begin Paint; Left; end; //левая сторона: for var n := 1 to h-1 do begin Paint; Up; end; end; Рис. 1. Путешествие по периметру 282
Главная часть программы и вовсе простая: begin Task('pp7'); Perimeter(5, 4); for var i := 1 to 6 do Down; Left; Perimeter(4, 3); for var i := 1 to 7 do Right; for var i := 1 to 5 do Up; Perimeter(4, 5); end. Задание pp8 Последнее задание опять довольно символическое (Рис. 1). Рис. 1. На-на? 283
Нарисовать букву Н можно разными способами, но важно, чтобы Робот возвращался в начальную клетку: procedure SymbolH(w, h, t: integer); begin //левая ножка: for var n := 1 to h do begin Paint; Down; end; //правая ножка: for var n := 1 to w - 1 do Right; for var n := 1 to h do begin Up; Paint; end; //перекладина: for var n := 1 to t - 1 do Down; for var n := 1 to w - 1 do begin Paint; Left; end; //домой: for var n := 1 to t - 1 do Up; end; В главной части программы мы вызываем процедуру SymboH с соответствующими значениями параметров и выполняем переходы между буквами: begin Task('pp8'); 284
SymbolH(4, 6, 3); for var i := 1 to 5 do Right; Down; SymbolH(5, 5, 4); for var i := 1 to 6 do Right; Up; Up; SymbolH(3, 7, 2); end. 285
Набор заданий examen В итоговом наборе 10 заданий, которые недоступны в диалоговом окне загрузки шаблонов, поэтому весь код придётся набирать вручную (или копировать из ранее выполненных заданий). Задание examen1 Робот должен закрасить 5 рядов клеток через одну (Рис. 1). Рис. 1. Чередуем 286
Самое простое решение – записать в процедуру Punktir код для закраски одного ряда клеток с возвратом к левой стене, а в главной части программы в цикле for вызвать эту процедуру 5 раз. Более интересное решение – все действия поместить в процедуру Punktir, а клетки закрашивать при движении Робота и слева направо, и справа налево. В этом случае код получится, конечно, сложнее: uses Robot; procedure Punktir(); begin for var row := 1 to 6 do begin if (row mod 2 = 1) then begin for var col := 1 to 7 do begin Paint; if (col < 7) then begin Right; Right; end; end; Down; Down; end else begin for var col := 1 to 7 do begin if (row < 6) then Paint; if (col < 7) then begin Left; Left; end; end; if (row < 6) then begin Down; Down; end; end; 287
end; end; begin Task('examen1'); Punktir; end. Задание examen2 Усложнённая версия первого задания: Робот должен закрасить клетки в шахматном порядке (Рис. 1). Рис. 1. Шашечки 288
Если вы написали простой вариант процедуры Punktir, то её вполне можно использовать и в этом задании, а все изменения перенести в цикл for. Сложный вариант этой процедуры потребует небольшой доработки: uses Robot; procedure Punktir(); begin for var row := 1 to 10 do begin if (row mod 2 = 1) then begin for var col := 1 to 7 do begin Paint; if (col < 7) then begin Right; Right; end; end; Down; end else begin Right; Right; for var col := 1 to 7 do begin Paint; if (col < 7) then begin Left; Left; end; end; if (row < 9) then Down; end; if (row < 10) then Left; end; end; begin 289
Task('examen2'); Punktir(); end. Задание examen3 От линейно-поступательного движения Робот переходит «периметрическому» (Рис. 1). Рис. 1. Закрутило 290
Обратите внимание, что Робот должен закончить работу в левом верхнем углу самого «дальнего» прямоугольника. Поэтому сначала нужно закрасить верхний левый «периметр», затем перейти в нижний и только потом в «целевой». Также следует учесть, что Робот должен вернуться в левый верхний угол прямоугольника. Процедура Perimeter очень проста и в пояснениях не нуждается: uses Robot; procedure Perimeter(n, m: integer); begin Paint; for var col := 1 to n - 1 do begin Right; Paint; end; for var row := 1 to m - 1 do begin Down; Paint; end; for var col := 1 to n - 1 do begin Left; Paint; end; for var row := 1 to m - 1 do begin Up; Paint; end; end; 291
Переходы между прямоугольниками в главной части программы можно организовать в виде циклов, но это необязательно: begin Task('examen3'); Perimeter(5, 4); Down; Down; Down; Down; Down; Down; Left; Perimeter(4, 3); Right; Right;Right;Right;Right;Right;Right; Up; Up; Up; Up; Up; Perimeter(4, 5); end. Задание examen4 «Экзаменационные» задания на процедуры закончены. Все остальные задания – на изменяемые поля. Это значит, что мы заранее не знаем, например, точных размеров прямоугольника, как в этом задании (Рис. 1). В таких случаях цикл с параметром for не годится, и следует использовать цикл с условием while. Условия движения вдоль стен очевидны, но мы ещё должны учесть, что в угловых клетках пути Робота стены отсутствуют, поэтому он просто делает 1 шаг в нужном направлении. 292
Рис. 1. Идём в обход uses Robot; begin Task('examen4'); Paint; Right; while (WallFromDown) do begin Paint; Right; end; Paint; Down; while (WallFromLeft) do 293
begin Paint; Down; end; Paint; Left; while (WallFromUp) do begin Paint; Left; end; Paint; Up; while (WallFromRight) do begin Paint; Up; end; end. Задание examen5 После «кругосветного» путешествия это задание Роботу покажется очень простым (Рис. 1). Чтобы не ошибиться, Робот может, например, считать шаги и закрашивать клетки через 1 шаг: uses Robot; begin Task('examen5'); var step := 0; //номер шага: while(FreeFromRight) do begin Right; step += 1; if (step mod 2 = 1) then Paint; 294
end; end. Рис. 1. Возвращение в коридор Задание examen6 В этом задании условия закрашивания клеток более интересные (Рис. 1)! Если внимательно проанализировать ситуацию на Рис. 1, то становится ясно, что Робот закрашивает клетки именно в двух случаях: 295
1. Когда под ним стена. 2. Когда над ним стена. Рис. 1. Вразброс В первом случае он делает шаг вниз, закрашивает клетку, а затем возвращается восвояси. Второй случай очевиден и противоположен: uses Robot; begin Task('examen6'); while(true) do begin 296
if (WallFromDown) then begin Up; Paint; Down; end; if (WallFromUp) then begin Down; Paint; Up; end; if (FreeFromRight) then Right else break; end; end. Задание examen7 Условия закрашивания клеток ещё усугубляются (Рис. 1)! Тут уже нужно смотреть в оба и несколько раз запускать задание, чтобы высмотреть все детали в подробностях! В итоге мы должны прийти к таким (умо)заключениям. Робот закрашивает клетку сверху, если: 1. Он стоит на закрашенной клетке, и сверху нет стены. 2. Он стоит на незакрашенной клетке, и стен нет ни сверху, ни снизу. Записать эти условия на языке, понятном всякому Роботу, не составит ни малейшего труда. 297
Рис. 1. Требования повышаются uses Robot; begin Task('examen7'); Right; while(true) do begin if ((CellIsPainted and FreeFromUp) or (CellIsFree and FreeFromDown and FreeFromUp)) then begin Up; Paint; 298
Down; end; if (FreeFromRight) then Right else break; end; end. Задание examen8 Для выполнения этого задания нужна, в первую очередь, хорошая память (Рис. 1). Рис. 1. Дежа-вю 299
Она поможет нам вспомнить задание cif16 (а), в котором Робот послушно ходил из одного угла в другой. Теперь он должен ещё прихватить с собой кисточку, чтобы закрасить угловую клетку: begin Task('examen8'); //если справа свободно, //идём вправо до стены: if (FreeFromRight) then while(FreeFromRight) do Right //если слева свободно, //идём влево до стены: else if (FreeFromLeft) then while(FreeFromLeft) do Left; //если сверху свободно, //идём вверх до стены: if (FreeFromUp) then while(FreeFromUp) do Up //если снизу свободно, //идём вниз до стены: else if (FreeFromDown) then while(FreeFromDown) do Down; Paint; end. Задание examen9 Здесь Робот, как таксист, работает на 2 счётчика (Рис. 1). Счётчики понадобятся ему при возвращении на базу, или, говоря потаксистски, в парк. 300
Идя в угол, Робот наматывает пробег на счётчик, а идя на базу – сматывает: Рис. 1. Задание на 2 счётчика uses Robot; procedure Row(n: integer); begin end; begin Task('examen9'); 301
var dx:= 0; var dy:= 0; while(FreeFromRight) do begin Right; dx += 1; end; while(FreeFromDown) do begin Down; dy += 1; end; //закрашиваем угловую клетку: Paint; //возвращаемся на базу: while(dx > 0) do begin Left; dx -= 1; end; while(dy > 0) do begin Up; dy -= 1; end; end. 302
Задание examen10 А стену можно обойти и с одним счётчиком (Рис. 1). Рис. 1. Огибаем Задание простое и неутомительное. Как для нас, так и для Робота: uses Robot; procedure Vert(w, h: integer); begin 303
end; begin Task('examen10'); var dx := 0; while(WallFromDown) do begin Right; dx += 1; end; Down; while(dx > 0) do begin Left; dx -= 1; end; end. На этом все задания закончились. Робот сделал своё дело и должен уйти… 304
Приложения В этой части книги собрана дополнительная информация, которая может пригодиться вам как при решении готовых заданий, так и при составлении собственных. Наборы заданий Всего для Исполнителя Робота имеются такие наборы заданий: а – система команд Робота (4) c – циклы for с параметром (16) if – условный оператор if (11) w – циклы while с условием (17) сif – циклы + логические выражения (22) count – переменные-счётчики (17) cc – вложенные циклы (19) p – процедуры без параметров (10) pp – процедуры с параметрами (8) mix – разные задачи на управляющие структуры (10) examen - задачи на процедуры с параметрами и изменяемые поля (10) (отсутствует в диалоговом окне) Итого: 10 (11) наборов и 134 (144) задания. 305
Система команд Робота Робот может выполнить 5 команд (Рис. 1). Рис. 1. Команды для Робота Right – идти вправо на 1 клетку Left – идти влево на 1 клетку Down – идти вниз на 1 клетку Up – идти вверх на 1 клетку Paint – закрасить клетку, на которой Робот стоит 306
Робот может с помощью «датчиков» определять наличие стен по границам клетки, на которой он стоит (Рис. 2). Рис. 2. Датчики Робота WallFromLeft – возвращает True, если слева от Робота стена WallFromRight – возвращает True, если справа от Робота стена WallFromUp – возвращает True, если сверху от Робота стена WallFromDown – возвращает True, если снизу от Робота стена 307
Эти же «датчики» сообщают Роботу, что стены вокруг его клетки отсутствуют (Рис. 3). Рис. 3. Датчики Робота FreeFromLeft – возвращает True, если слева от Робота стена отсутствует FreeFromRight – возвращает True, если справа от Робота стена отсутствует FreeFromUp – возвращает True, если сверху от Робота стена отсутствует FreeFromDown – возвращает True, если снизу от Робота стена отсутствует 308
Другой датчик Робота – цветовой – определяет, закрашена ли клетка, на которой Робот стоит (Рис. 4). Рис. 4. Цветовой датчик Робота CellIsFree – возвращает True, если клетка с Роботом не закрашена CellIsPainted – возвращает True, если клетка с Роботом закрашена 309
Советы по решению заданий • Заведите для решений отдельную папку, чтобы потом не искать файлы на диске. • Внутри этой папки вы можете создать папки для каждого решения. • Чтобы при загрузке новых заданий они автоматически сохранялись в нужной папке, сначала запишите в неё одно задание, а затем, приступая к работе, запускайте среду двойным щелчком по файлу решения из этой папки. • Выполняйте задания последовательно, так вы лучше отработаете навыки программирования. • Внимательно читайте условия выполнения задания. • Многие задачи можно решить несколькими способами. Некоторые из них могут быть эффективнее, чем это необходимо. Но ваша задача – отработка вполне определённых конструкций языка, а не разработка эффективных алгоритмов, поэтому отдавайте предпочтение простым алгоритмам. • С другой стороны, и в рамках предложенных условий задачу можно решить несколькими способами. Ищите их, это поможет вам в будущем не останавливаться на первом пришедшем в голову способе решения, а искать различные подходы к решению проблемы. • При разработке алгоритма очень удобно держать задание перед собой. Для этого запустите программу командой Программа > Выполнить без связи с оболочкой (Рис. 1). • Набирая текст в Редакторе кода, вы сможете мысленно проверять его работу по изображению на экране. 310
Рис. 1. Отличный вид! • Но перед запуском программы сначала закройте это окно! • Разрабатывайте алгоритм частями и сразу проверяйте, как Робот выполняет команды. Для этого используйте пошаговый режим работы программы. Если в действиях Робота обнаружатся ошибки, сразу исправляйте их и снова проверяйте написанный код. • Если, например, Робот должен закрасить клетки по периметру прямоугольного поля, то сначала напишите часть кода для закрашивания клеток верхней стороны прямоугольника. Если он работает правильно, переходите к закрашиванию правой стороны, и так далее. • Если алгоритм движения Робота трудно представить мысленно, то перерисуйте поле на бумагу и проведите его путь. Такой способ поможет вам найти закономерности в его действиях. • Если алгоритм почти отлажен, переходите на автоматический режим, чтобы не нажимать многократно кнопку Шаг. 311
• По мере отладки алгоритма увеличивайте скорость движения Робота, чтобы не тратить время понапрасну. • В заданиях с изменяемыми параметрами проверяйте действие алгоритма на всех принципиально разных полях. Вполне возможно, что на некоторых полях ваш алгоритм работает правильно, а на других с ошибками. • Используйте в проектах уже готовый и отлаженный код из предыдущих заданий, так вы сэкономите время на разработку алгоритма и написание нового кода. • Ещё лучше и правильнее завести «копилку» элементарных типовых действий, из которых потом можно составлять сложные алгоритмы. 1. Например, очень часто Робот должен дойти до стены. Стены могут быть в любом направлении, поэтому для определённости будем считать, что стена находится справа от Робота (Рис. 2). Рис. 1. Идём до стены 312
После выполнения следующего кода Робот остановится возле правой стены (Рис. 3): //Робот идёт до правой стены: while(FreeFromRight) do Right; Рис. 2. Тютелька в тютельку 313
2. Робот идёт до левого верхнего угла (Рис. 3). Рис. 3. Пора в угол В этом случае Робот сначала идёт до левой стены, а затем до верхней (или наоборот) (Рис. 4): //Робот идёт до левой стены: while(FreeFromLeft) do Left; //Робот идёт до верхней стены: while(FreeFromUp) do Up; 314
Рис. 4. Нашёл своё место 315
3. Робот идёт вправо и закрашивает 5 клеток (Рис. 5). Рис. 5. Раск-раск-а В данном случае Робот останавливается на последней закрашенной им клетке (Рис. 6): //Робот идёт вправо и //закрашивает 5 клеток: for var i := 1 to 5 do begin Paint; if (i < 5) then Right; end; 316
Рис. 6. Главное – вовремя останоновиться! Если он после закрашивания клеток продолжает движение в том же направлении, то условный оператор if нужно убрать. 317
4. Робот идёт вправо до стены и закрашивает клетки (Рис. 7). Рис. 7. Закрашиваем до упора в стену Если Робот стоит на закрашиваемой клетке, то ему придётся дополнительно закрасить клетку у самой стены, так как после окончания цикла while он только дойдёт до неё (Рис. 8): //Робот идёт до правой стены //и закрашивает клетки: while(FreeFromRight) do begin Paint; Right; 318
end; //Робот закрашивает клетку у стены: Paint; Рис. 8. Сдадим в срок Робот может сначала закрасить клетку, а затем пойти вправо. Тогда порядок команд в цикле while необходимо изменить: //Робот закрашивает первую клетку: Paint; //Робот идёт до правой стены //и закрашивает клетки: 319
while(FreeFromRight) do begin Right; Paint; end; Если Робот стоит на незакрашенной клетке, то первую команду Paint следует убрать. Понятно, что совершенно невозможно (да и не нужно!) перечислить все миниалгоритмы, которые могут понадобиться при решении заданий, но вы можете самостоятельно пополнять этот список по ходу решения задач. Разработка собственных наборов заданий Если вы хотите придумать собственные задания, то нет ничего проще! Это может быть и одиночное задание, и целый набор. Второй вариант предпочтительнее, так как не стоит плодить без нужды лишние файлы. В любом случае для заданий потребуется отдельный модуль. Создайте новый файл и запишите его в нужную папку под тем же именем, что и название модуля. Для примера, файл модуля: RVRobotTasks.pas Название модуля: unit RVRobotTasks; Для разработки заданий потребуется модуль RobotTaskMaker: 320
uses RobotTaskMaker; В главной части собственного модуля нужно зарегистрировать все задания и группу заданий: begin RegisterTask('rvrobot1', RVRobot1); RegisterTask('rvrobot2', RVRobot2); RegisterTask('rvrobot3', RVRobot3); RegisterTask('rvrobot4', RVRobot4); RegisterGroup('rvrobot', 'Новые задания для Робота', 'RVRobotTasks', 4); end. В этом модуле 4 задания. Каждое задание нужно зарегистрировать, передав процедуре RegisterTask название задания (любое – по вашему выбору) и имя процедуры с заданием: RegisterTask(name: string; p: TaskProcType); Группа заданий регистрируется с помощью процедуры RegisterGroup: RegisterGroup(name,description,unitname: string; count: integer); Здесь сначала указывается название группы, затем короткое описание и название модуля и, наконец, число заданий в наборе. Несмотря на регистрацию, группа заданий не появляется в диалоговом окне выбора заданий, поэтому заготовку программы придётся создавать самостоятельно. 321
Лучше записать шаблон на диск, а затем копировать его для каждого нового задания. Все задания оформляются в виде отдельных процедур: procedure RVRobot1; begin TaskText('Задание rvrobot1. Закрасить помеченные клетки'); Field(11, 11); RobotBegin(1, 1); RobotEnd(11, 11); for var row := 1 to 11 do for var col := 1 to row do Tag(col, row); end; procedure RVRobot2; begin TaskText('Задание rvrobot2. Закрасить помеченные клетки'); Field(11, 11); RobotBegin(1, 1); RobotEnd(1, 11); for var row := 1 to 11 do for var col := 1 to 12 - row do Tag(col, row); end; procedure RVRobot3; begin TaskText('Задание rvrobot3. Закрасить клетки у стены'); Field(11, 11); RobotBegin(1, 1); RobotEnd(1, 11); for var row := 1 to 11 do begin var col := Random(9) + 2; VerticalWall(col, row - 1, 1); Tag(col, row); end; end; procedure RVRobot4; 322
begin TaskText('Задание rvrobot4. Закрасить клетки за стеной'); Field(11, 11); RobotBegin(1, 1); RobotEnd(1, 11); var col := 0; for var row := 1 to 11 do begin var tmp := Random(8) + 2; while (col = tmp) do tmp := Random(8) + 2; col := tmp; VerticalWall(col, row - 1, 1); Tag(col + 1, row); end; end; Поскольку модуль для проверки правильности работы кода запустить нельзя, то параллельно создавайте файлы с решениями задач. Они должны включать список используемых модулей: uses Robot, RVRobotTasks; А в главной части программы следует вызвать задание, указав его название: begin Task('rvrobot1'); end. Если вы запустите этот код, то увидите задание в том виде, в котором его создаёт процедура RVRobot1 (Рис. 1). 323
Рис. 1. Тест Если вы обнаружите неточности или ошибки в работе процедуры, то исправляйте их в модуле с заданиями и снова запускайте файл с решением. Задания после отладки можно решать точно так же, как мы это делали раньше. Откомпилированный модуль RVRobotTasks.pcu (он создаётся автоматически при запуске файла с решением или при выполнении команды меню Программа > Компилировать) можно скопировать в отдельную папку, в которой будут храниться файлы с решениями задач или в системную папку на диске С PascalABC.NET/ Lib. 324
Новое задание, естественно, сначала нужно придумать, а затем написать соответствующую процедуру, используя команды из модуля RobotTaskMaker. Напечатать текст задания: TaskText(s: string); s – поясняющая строка, которая появляется в верхней части окна с заданием. Начертить поле заданных размеров: Field(szx,szy: integer); szx, szy – размеры поля в клетках: ширина/высота. Начертить горизонтальную стену: HorizontalWall(x,y,len: integer); x, y – координаты начала стены на поле в клетках. Координата х на 1 меньше номера клетки, в которой стена начинается. Координаты клеток отсчитываются от 1. len – длина стены в клетках. Начертить вертикальную стену: VerticalWall(x,y,len: integer); x, y – координаты начала стены на поле в клетках. Координата y на 1 меньше номера клетки, в которой стена начинается. Координаты клеток отсчитываются от 1. len – длина стены в клетках. 325
Начертить Робота в начальной позиции: RobotBegin(x,y: integer); x, y – координаты начальной клетки пути Робота. Начертить конечную позицию Робота: RobotEnd(x,y: integer); x, y – координаты конечной клетки пути Робота. Обозначить стартовую и финишную позицию Робота: RobotBeginEnd(x,y,x1,y1: integer); Объединяет процедуры RobotBegin и RobotEnd. Обозначить точкой закрашиваемую клетку: Tag(x,y: integer); x, y – координаты клетки с точкой, обозначающей клетку, которую Робот должен закрасить. Обозначить точками прямоугольник закрашиваемых клеток: TagRect(x,y,x1,y1: integer); x, y – координаты левого верхнего угла прямоугольника. 326
x1, y1 – координаты правого нижнего угла прямоугольника Обозначить закрашенную клетку: MarkPainted(x,y: integer); x, y – координаты закрашенной клетки зелёного цвета. Рис. 2. Все команды в наглядном виде На всякий случай составим задание для проверки данных на Рис. 2⬆: procedure tipps5; 327
begin TaskText('Задание tipps5. Закрасить клетки'); Field(8, 11); RobotBegin(7, 1); RobotEnd(8, 3); Tag(8,5); TagRect(4,7,7,9); MarkPainted(8,11); HorizontalWall(0,2,5); VerticalWall(1,5,5); end; На Рис. 3 вы можете видеть, что рисунок правильный! Рис. 3. Успешная проверка 328
Набор RVRobot Этот набор заданий преследует единственную цель: показать на примерах, как создавать собственные задания для Исполнителя Робота. Задание rvrobot1 Нехитрое, но увлекательное задание на закрашивание клеток треугольником, подобное тому, что мы уже решали раньше (Рис. 1). Рис. 1. Полная косынка 329
Решение задачи простое, но нужно учесть условия перемещения Робота вниз и к левой стене: uses Robot, RVRobotTasks; begin Task('rvrobot1'); for var row := 1 to 11 do begin for var col := 1 to row do begin Paint; if (col < row) then Right; end; //Робот возвращается к левой стене: while ((row < 11) and FreeFromLeft) do Left; if (FreeFromDown) then Down; end; end. 330
Задание rvrobot2 Как предыдущее задание, но треугольник перевёрнут вверх основанием (Рис. 1). Рис. 1. Вверх тормашками Решение легко получить из предыдущего задания, исправив несколько строк: uses Robot, RVRobotTasks; begin Task('rvrobot2'); 331
for var row := 1 to 11 do begin for var col := 1 to 12-row do begin Paint; if (col < 12-row) then Right; end; //Робот возвращается к левой стене: while ((row < 11) and FreeFromLeft) do Left; if (FreeFromDown) then Down; end; end. Задание rvrobot3 В этом задании появляются стены (Рис. 1). Рис. 1. Об стену 332
Чтобы не разбить лоб о стену, Робот вызывает процедуру FreeFromRight. Дойдя до стены, Робот закрашивает клетку, а потом тем же макаром возвращается к левой стене: uses Robot, RVRobotTasks; begin Task('rvrobot3'); for var row := 1 to 11 do begin while(FreeFromRight) do Right; Paint; //Робот возвращается к левой стене: while (FreeFromLeft) do Left; if (FreeFromDown) then Down; end; end. 333
Задание rvrobot4 За стеной всегда интереснее! Поэтому Робот должен закрасить клетки, которые «прячутся» за стеной (Рис. 1). Рис. 1. Заглянем за стену «Тонкость» решения заключается в том, что верхнюю стену Робот должен обойти снизу, а нижнюю – сверху. Все остальные стены можно обходить в любом направлении. В программе Робот обходит их сверху: uses Robot, RVRobotTasks; 334
begin Task('rvrobot4'); for var row := 1 to 11 do begin while(FreeFromRight) do Right; //первая горизонталь: if (row = 1) then begin Down; Right; Up; Paint; Down; Left; Up; end else begin Up; Right; Down; Paint; Up; Left; Down; end; //Робот возвращается к левой стене: while (FreeFromLeft) do Left; if (FreeFromDown) then Down; end; end. 335
Литература Журнал Смекайлик: занимательное программирование на языках Паскаль2015 и Си-шарп в среде PascalABC.NET. 336