Journal influence
Bookmark
Next issue
Abstract:
Аннотация:
Author: () - | |
Ключевое слово: |
|
Page views: 10442 |
Print version |
Очень часто, решая ту или иную проблему, мы оказываемся в шкуре буриданова осла. Задача имеет два альтернативных решения, словно две охапки сена слева и справа от упомянутого животного. Доводы за и против каждого уравновешены. Как в этом случае поступить? Ехать или не ехать отдыхать на юг? Строить или не строить дачу? Поистине гамлетовские вопросы задает нам жизнь! Некоторые в таких ситуациях бросают монету, другие загадывают мужчину или женщину и смотрят в окно, ожидая, представитель какого пола появится первым. Но все это не научные методы. Монетка может куда-нибудь закатиться, а по улице, как назло, за целый час кошка только и пробежит. Есть проверенный столетиями метод принятия подобных решений: разложить пасьянс. Сошелся — решение принято, все сомнения прочь. Можно вдобавок подыскать доводы в его пользу, и оно будет твердо воплощаться в жизнь. Пасьянс психологически нас на это настраивает. Принять решение таким способом иногда бывает трудно, так как не всегда под рукой есть колода карт. Но это можно сделать и на дисплее ЭВМ. Мои знакомые, у которых я часто бываю, так и поступают. Машина у них отменная: может исполнять музыку, имеет клавиши с символами карточных мастей. Вообще символика у этой машины очень непривычная. Литерные переменные обозначаются не Ц, а знаком $. В операторе NEXT, завершающем цикл, можно не указывать его индекс. Однако читать программы бывает нелегко. Ребята составили игровую программу по правилам пасьянса «Турецкий платок» и раскладывают его, когда нужно принять какое-то решение. Правила у этого пасьянса такие. Из одной колоды карт в 52 листа выкладывают картинкой вверх пять рядов по 10 карт в каждом. Последние две карты кладут в шестой неполный ряд на любое место, как правило, к первому и второму столбцам. Получается нечто действительно похожее на цветастый платок. Требуется распустить его, снимая за один ход по две нижние карты одного достоинства из разных столбцов. Нумерация массивов в ЭВМ, на которой работают ребята, идет от нуля. Поэтому первый ряд в программе значится как нулевой, второй — как первый и так далее. То же самое со столбцами. Вот как может выглядеть раскладка пасьянса после того, как сняты две пары карт. По правилам игры сейчас можно снять либо двух королей, либо двух дам. Для этого нужно нажать в любом порядке две клавиши — 0 и 2 или 1 и 3, что соответствует номерам столбцов, которые этими картами замыкаются. Изображения снимаемых карт на дисплее сотрутся. Следующим ходом можно снять две двойки, нажав клавиши 0 и 8. Если удастся снять все 52 карты, то машина в награду сыграет бравурную мелодию. Это означает, что на юг все-таки ехать нужно, а дачу следует строить. Если же по ходу игры станет ясно, что платок распустить невозможно, то остановить игру можно нажатием клавиши S (STOP). В таком случае машина воспроизведет лишь музыкальную гамму. С положительным решением проблемы пока нужно повременить. Я взял распечатку программы — взгляните. Прочли и сразу все поняли? Лично мне это долго не удавалось. Дело не в символике. Какой смысл имеют все массивы? Как они формируются? Как изменяются по ходу работы программы? Вот в чем трудно было разобраться. Но когда я во всем разобрался, то восхитился: чудесная все-таки вещь — литерные массивы! Для тех, кто затрудняется с чтением программы, поясню ее работу. Массивы К$() и М$() — это заготовки, из которых образуются обозначения карт на дисплее. Первый — это достоинства карт, второй — масти. Все карты в колоде пронумерованы числами от 1 до 52. Номер карты V определяет и достоинства ее, и масть. Той же буквой обозначен индекс массива K(V). Перед раскладкой пасьянса все его элементы равны нулю. Как только карта с каким-то номером V вынута из колоды и заняла свое место в пасьянсе, соответствующий элемент K(V) становится равным единице. Зачем? Затем, чтобы в пасьянсе не было одинаковых карт. Ведь номер V выдается датчиком случайных чисел, а повторения в их ряду вполне возможны. Но сразу же после выдачи числа V в программе происходит проверка: не равен ли единице элемент K(V)? To есть не вытаскивалась ли уже эта карта? Место каждой карты в пасьянсе выражается двумя индексами — I и J. Первый индекс — номер ряда, второй — столбца. Эти же индексы входят в обозначения массива B$(l, J). Он характеризует достоинство карты, лежащей в l-м ряду и J-м столбце. Почему только достоинство? Потому что только этот признак должен совпадать у снимаемых карт. Только по нему сравниваются программой карты, когда их хочет снять играющий. Не совпали достоинства — карты остаются на дисплее. Пасьянс раскладывается ряд за рядом. Перед тем как положить карту на место в очередном l-м ряду и каком-то J-столбце, программа и запрашивает датчик случайных чисел. Когда тот выдаст число V, по нему тут же вычисляется достоинство карты К$() и ее масть М$(). Значение К$ присваивается элементу B$(l, J). Когда играющий хочет снять карту, он указывает номер соответствующего столбца. Нижняя карта в нем стирается с дисплея, и величина N(J) уменьшается на единицу. N(J) — это количество карт в J-м столбце. Непросто, правда? Между прочим, это было только общее описание игры. Теперь начнем читать программу — строка за строкой. Строка 1. Здесь отводится место для хранения числовых и литерных массивов. Все они уже были представлены в общем описании, кроме одного — J (I). Здесь I — номер очередного хода игрока, а J (I) — номер столбца, из которого этим ходом снимается карта. А переменная Н? Это счетчик снятых карт. В самом начале игры, естественно, Н=0. Строка 2. Список, элементы которого — символы достоинства и масти карт. Строка 3 Эти символы считываются и становятся элементами массивов К$ (I) и М$ (I). Оператор PRINT CHR $ (22) очищает экран. Между прочим, оба эти массива — литерные. Числовым массив является только тогда, когда все без исключения его элементы — числа. А в массиве К$ (I) есть и буквы. Массив М$ (I) и подавно литерный. Его элементы — даже и не буквы. Строки 4-5.- На дисплее выписываются номера столбцов раскладки — от нулевого до девятого. Задается длина каждого столбца N ( ). В первых двух — по шесть карт (ряды с нулевого по пятый). В следующих — по пять (ряды с нулевого по четвертый). Два оператора PRINT. За ними — пустота. Что же тогда они выводят на дисплей? Ничего. Просто дважды переводят курсор в начальную позицию нижеследующей строки. Cтрока 6. Начинается раскладка пасьянса. Цикл в цикле. FOR l=0 TO 5:FOR J=0 TO 9. Шесть рядов, десять столбцов. Итого 60 мест. Но в колоде не 60 карт, а 52. Как прекратить раскладку? Последний цикл по 1 прерывается при J=2. Для этого в конце строки 6 поставлена логическая команда IF 1=5 AND J=2 GOTO 10 Строка-7. Выдается случайное целое число V из промежутка от 1 до 52. Проверяется, не равен ли единице соответствующий элемент массива K(V). Если равен, будет затребовано новое случайное число: IF K(V) = 1. Это гарантирует, что в раскладке не будет двух одинаковых карт. Строка 8. Если мы добрались до этой строки, значит, только что выданное в предыдущей строке число V до сих пор еще не попадалось. Чтобы оно не повторялось, принимаются соответствующие меры: K(V)=1. А дальше... Между прочим, над тем, что написано в этой строке дальше, я ломал голову столько же времени, сколько над всеми остальными строками, вместе взятыми. Понимал, что тут по номеру карты рассчитывается ее достоинство и масть. Но как это делается? Наконец догадался: весь ряд из 52 чисел разбит на четыре равных диапазона. Их номерам поставлены в соответствие элементы массива мастей Щ (I). Первый диапазон — черви. Второй — трефы. Третий — бубны. Четвертый — пики. В каждом диапазоне числа поставлены в соответствие элементам массива достоинства К$ (I) — от двойки до туза. Вся суть такого разбиения заключена в выражении INT ((V—1)/К). Оно на единицу меньше номера диапазона, в который попадает число V. Я проверил это на добром десятке примеров. Поэтому сумма 1 + INT ((V—1 )/13) в точности равна номеру диапазона. А стало быть,, элемент М$ (1 +INT((V—1 )/13) — это масть карты с номером V. Произведение 13* INT((V-1)/13) — это ближайшая слева к номеру V граница диапазона. Разность V-13* INT((V-1)/13) равна порядковому номеру карты внутри диапазона. В массиве К$ (I) берется элемент с таким номером. Он определяет достоинство карты и становится значением очередного элемента массива B$(I,J). Оба элемента — K$(l) и M$(l) — выводятся на дисплей оператором PRINT" "K$(N) M$(1+INT((V-1)/K)). Перед ними, судя по виду этого оператора, выводится пробел. Так на дисплее возникают парные символы карт. Разделенные пробелами, они не сливаются, а располагаются по столбцам. Строка 9. Эта непривычная символика! В привычном варианте, конечно, было бы так: NEXT J:PRINT:NEXT I. Когда индекс J достигает наибольшего значения, курсор переводится в начальное положение следующей строки и увеличивается индекс I. С каждым приростом индекса J или I происходит возврат на строку 6. Берется следующее случайное число V. Выкладывается новая карта. И так пока не будет разложен весь пасьянс. Строка 10. Музыка! В и С — это обозначения нот си и до. По команде MUSIC «ВС» машина воспроизводит эти звуки гаммы. И начинается цикл. Он будет пройден два раза. Ясно: это затем, чтобы играющий мог указать номера двух столбцов, в которых он хочет снять карты. Но где же конец цикла? Ага, в строке 15. Что же до нее? Строка 11. Чуткий оператор GET. Строка выполняется снова и снова, пока не нажмешь какую-то клавишу. Строка12. Если это клавиша S, то значит STOP — управление передается на строку 19. Передается с заданием исполнить какую-то мелодию — догадываюсь по виду строки в кавычках. Какую? CDEF-GAB —до-ре-ми... — это гамма. Я помню, как она звучала, когда я нажимал клавишу S. А нажимал я ее, когда становилось ясно, что распустить «турецкий платок» невозможно. Если нажата другая клавиша, управление передается на следующую строку. Cтpока 13 Если нажата не цифровая клавиша, машина переходит на строку 11 и снова ждет ввода. Если цифровая — спускается еще на строку. Строка 14. Символ, обозначенный на нажатой клавише, переводится в число. Дальше IF N(J(1))=-1. Что бы это значило? N(J(I)) — это длина столбца, из которого снимается очередная карта. Отрицательная длина столбца? Как это может получиться? Ищу строку программы, где уменьшается величина N(J($). Нашел в строке 17: N(J(I))=N(J(I))-1. А перед этим — PRINT"". Вывести пробел — значит стереть. То есть по смыслу игры — снять карту. Чтобы величина N(J(I)) стала равной -1, до этого она должна равняться 0. Все ясно: N(J(I))=-1 означает попытку снять карту из уже пустого столбца. Условный оператор в строке 14 пресекает эту попытку и дает возможность исправиться — передает управление на строку 11. Cтрока 15 «Музыкальный» оператор, нота ля. Длительность звучания устанавливается в первой строке оператором TEMPO с последующей цифрой. Вспоминаю: короткий звуковой сигнал был условным знаком того, что карта снята правильно. NEXT — переход на строку 10, чтобы играющий мог снять еще одну. Строка 16. Машина начеку! Она проверяет, не пытается ли игрок обмануть ее, снять карты разного достоинства или из одного столбца. Если так, то GOTO 10 — звуковой сигнал си-до — просьба повторить ход. Если же все сделано правильно, то переход на следующую строку. Строка 17. Снова цикл, который проходится дважды. Понятно: настало время стирать изображения снимаемых карт с дисплея. CURSOR — оператор установки курсора. Куда? 3*J(I), 3+N(J(l)). Опять вычисления! Впрочем, разобраться удается и в них: курсор устанавливается в нижнюю позицию J-ro столбца. PRINT"": стирается то, что размещается на этой позиции. NEXT: от 1 =0 переход к 1=1, возврат к началу строки. Тем же оператором PRINT"" снимается еще одна карта. Н=Н+2: счетчик снятых карт увеличился на 2. CURSOR 10,10:PRINT H. Курсор перемещается на заданное место. Там печатается число снятых карт. IF H<52 THEN MUSIC "CDCDCD":GOTO 10. Если карты сняты не все, звучит МАТЧИШ {до-ре-до-ре-до-ре) и дается возможность сделать новый ход. Строка 18. Сняты все карты и... Строка 79 И машина дважды (FOR 1=1 ТО 2) играет ... начало «Турецкого рондо» Моцарта. Именно эта мелодия записана в строке 18 в виде литерной константы, значение которой присваивается переменной А$. Непросто, очень даже непросто, правда? А распускать «Турецкий платок», сидя за дисплеем, одно удовольствие. |
Permanent link: http://swsys.ru/index.php?id=1497&lang=en&page=article |
Print version |
The article was published in issue no. № 4, 1988 |
Perhaps, you might be interested in the following articles of similar topics:
- Искусственный интеллект в грядущем десятилетии
- Интерактивная процедура построения модели тренда для экономических показателей
- Целесообразность применения web-служб в распределенных автоматизированных системах военного назначения
- Реализация теней с помощью библиотеки OpenGL
- Время перемещать камни
Back to the list of articles