Код Hamurabi на Perl'е

Aug 08, 2011 17:28

Это не про прославленный свод законов шумерского царя Хаммурапи (Hammurabi Code), а о коде некогда популярной игры Hamurabi. Одна буковка m выпала из-за того, что до нашей эры имя файла состояло максимум из 8 букв. По изысканиям yarikas корни этой игры уходят в 50-е гг, так что исторически это одно из первых клиодинамических приложений :-)

Захотелось посмотреть что это было, но перловой версии под руку как-то не попалось. Чаще всего выложены коды для Бейсика, видел джавовый, С и даже действующий Джаваскрипт - можно поиграть онлайн :-) Перловку пришлось варить самому. Взял топор код на Бейсике и тупо-построчно заменил на соответствующие операторы Perl'а. Благо, что в Perl'е тоже есть оператор goto :-) Бейсиковые строчки я не удалял, а только закомментировал - можно сравнить при желании здесь
http://vlkamov.livejournal.com/722060.html

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

Для удобства я использовал самопальный русифицированный диалект Perl'а. После перевода Hamurabi на русскоперловый язык и некоторой систематизации код игры стал существенно более прозрачным. Это позволило побороть почти все "макароны", остались две малосущественные, описывающие экстремальное увольнение. Вся программа разбита на последовательно исполняемые блоки с ясными функциями. Для красоты кода пришлось немножко изменить порядок исполнения блоков и привести его в соответствие естественному ходу вещей. В оригинальном коде первый годовой цикл отрабатывается без участия игрока, так что диалог начинается годового отчета. Я не поленился отчет перенести в конец, так что игрок отдает распоряжения с первого же года, а итоги года подводятся в его конце. Здесь обсуждение результата переработки и истоков.
http://vlkamov.livejournal.com/722270.html
По адресу
http://vlkamov.narod.ru/hamurabi_2.zip
можно найти уже оттранслированный в латиницу перловый скрипт. Кодировка KOI8-R сохранила некотрую читабельность.

В новой версии чума, голодомор и прирост населения вынесены в отдельный демографический блок. Интересно отметить, что в формуле прироста население стоит в знаменателе, что заставляет сомневаться в ориентации авторов. При сборке-разборке традиционно остались лишние детали, например:
$C = 0; # ?????????
Что делает эта вспомогательная переменная в операциях с землей и распределении зерна, понять так и не удалось.



#!/usr/bin/perl
#hamurabi_2.pl

Начальные_условия();
foreach $ГОД (1..10){
Операции_с_землей();
Распределение_зерна();
Планирование();
Сельхоз_цикл();
Демограф_цикл();
Годовой_отчет();
}
Итоги_десятилетия();
goto Выход;

Уволить_за_наглость:
print "\nХаммурапи: я не могу сделать то, что ты хочешь.\nЗаменю тебя другим управляющим !!!!!";
goto Выход;

Досрочное_увольнение:
print "Ты уморил голодом ", $УМЕРЛО_ОТ_ГОЛОДА, " человек за год !!!\n";
print "Из-за чрезвычайно плохого управления ты сейчас же уволен, \nвыброшен из резиденции и\n объявлен \"Предателем нации\" !!";
goto Выход;

Выход:
print "\nБиииииииииииип\nПока.\n\n";
exit;

# =====================================

sub Начальные_условия{
$УМЕРЛО_ОТ_ГОЛОДА_10 = 0; # за 10 лет нарастающим итогом
$СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 = 0;
$ГОД = 0;
$НАСЕЛЕНИЕ = 100;
$ПРИРОСТ_НАСЕЛЕНИЯ = 5;
$Q = 1;
$D = 0;
$ПЛОЩАДЬ = 1000;
$ЗЕРНО_В_АМБАРЕ = 2800;
print "Хаммурапи: поручаю тебе управление поселением в течение 10 лет. На начало периода:\n";
print "население составляет ", $НАСЕЛЕНИЕ, " человек\n";
print "Город владеет ", $ПЛОЩАДЬ, " акрами плодородной земли.\n";
print "Запасы зерна составляют ", $ЗЕРНО_В_АМБАРЕ, " бушелей.\n";
}

# =====================================

sub Операции_с_землей{
# операции с землей
$ЦЕНА_ЗЕМЛИ = int (10*rand()) + 17; # 17..26
print "Земля торгуется по ", $ЦЕНА_ЗЕМЛИ, " бушелей за акр.\n";
Покупка_земли:
print "Сколько акров ты намерен купить ? ";
$Q = ;
goto Уволить_за_наглость if $Q<0;
goto Продажа_земли if $Q==0;
if ($ЦЕНА_ЗЕМЛИ*$Q > $ЗЕРНО_В_АМБАРЕ){
print "Хаммурапи: думай снова. У тебя только \n", $ЗЕРНО_В_АМБАРЕ, " бушелей зерна. Итак, \n";
goto Покупка_земли;
}
$ПЛОЩАДЬ = $ПЛОЩАДЬ + $Q; $ЗЕРНО_В_АМБАРЕ = $ЗЕРНО_В_АМБАРЕ - $ЦЕНА_ЗЕМЛИ*$Q;
$C = 0; # ?????????
goto Закончить_операции_с_землей;

Продажа_земли:
print "Сколько акров ты намерен продать ? ";
$Q = ;
goto Уволить_за_наглость if $Q<0;
goto Закончить_операции_с_землей if $Q==0;
if ($Q > $ПЛОЩАДЬ) {
print "Хаммурапи: думай снова. У тебя только \n", $ПЛОЩАДЬ, " акров земли. Итак, \n";
goto Продажа_земли;
}
$ПЛОЩАДЬ = $ПЛОЩАДЬ - $Q; $ЗЕРНО_В_АМБАРЕ = $ЗЕРНО_В_АМБАРЕ + $ЦЕНА_ЗЕМЛИ*$Q;
$C = 0; # ?????????
goto Закончить_операции_с_землей;

Закончить_операции_с_землей:
}

# =====================================

sub Распределение_зерна{
print "\n";
Выделить_зерно:
print "Сколько зерна пойдет на продовольствие ?";
$ПРОДОВОЛЬСТВЕННОЕ_ЗЕРНО=;
goto Уволить_за_наглость if $ПРОДОВОЛЬСТВЕННОЕ_ЗЕРНО < 0;
if ($ПРОДОВОЛЬСТВЕННОЕ_ЗЕРНО > $ЗЕРНО_В_АМБАРЕ){
print "Хаммурапи: думай снова. У тебя только \n", $ЗЕРНО_В_АМБАРЕ, " бушелей зерна. Итак, \n";
goto Выделить_зерно;
}
$ЗЕРНО_В_АМБАРЕ = $ЗЕРНО_В_АМБАРЕ - $ПРОДОВОЛЬСТВЕННОЕ_ЗЕРНО;
$C = 1; # ?????????
print "\n";
}

# =====================================

sub Планирование{
Назначить_площадь_посевов:
print "Сколько акров ты намерен засеять ?";
$ПЛОЩАДЬ_ПОСЕВОВ = ;
goto Уволить_за_наглость if $ПЛОЩАДЬ_ПОСЕВОВ < 0;

if ($ПЛОЩАДЬ_ПОСЕВОВ > $ПЛОЩАДЬ ) {
print "Хаммурапи: думай снова. У тебя только \n", $ПЛОЩАДЬ, " акров земли. Итак, \n";
goto Назначить_площадь_посевов;
}

# норма посева - 1 бушель на 2 акра
if (int($ПЛОЩАДЬ_ПОСЕВОВ/2) > $ЗЕРНО_В_АМБАРЕ){
print "Хаммурапи: думай снова. У тебя только \n", $ЗЕРНО_В_АМБАРЕ, " бушелей зерна. Итак, \n";
goto Назначить_площадь_посевов;
}

# норма обслуживания - 10 акров на человека
if ($ПЛОЩАДЬ_ПОСЕВОВ > 10*$НАСЕЛЕНИЕ) {
print "Но у тебя только ", $НАСЕЛЕНИЕ, " человек чтобы обрабатывать поля. Итак, \n";
goto Назначить_площадь_посевов;
}
}

# =====================================

sub Сельхоз_цикл{
# норма посева - 1 бушель на 2 акра
$ЗЕРНО_В_АМБАРЕ = $ЗЕРНО_В_АМБАРЕ - int($ПЛОЩАДЬ_ПОСЕВОВ/2);
# вегетационный период
$Y = int(rand()*5) + 1; # урожайность 1..5 бушелей с акра, в среднем 3
# сбор урожая и засыпка зерна на хранение
$УРОЖАЙ = $ПЛОЩАДЬ_ПОСЕВОВ*$Y;
#потери урожая 2 года из пяти
$C = int(rand()*5) + 1; # 1..5
if (int($C/2) != $C/2){
# если нечетное $С потерь нет
$СЪЕДЕНО_КРЫСАМИ = 0;
}
else {
$СЪЕДЕНО_КРЫСАМИ = int($ЗЕРНО_В_АМБАРЕ/$C); # в среднем 3/20 остатка
}
$ЗЕРНО_В_АМБАРЕ = $ЗЕРНО_В_АМБАРЕ - $СЪЕДЕНО_КРЫСАМИ + $УРОЖАЙ;
}

# =====================================

sub Демограф_цикл{
# странно - обратно пропорционально населению
$C = int(rand()*5) + 1; # 1..5
$ПРИРОСТ_НАСЕЛЕНИЯ = int($C*(20*$ПЛОЩАДЬ + $ЗЕРНО_В_АМБАРЕ)/$НАСЕЛЕНИЕ/100 + 1);
# пропитание 20 бушелей в год на человека
$ОБЕСПЕЧЕНО_ПИЩЕЙ = int($ПРОДОВОЛЬСТВЕННОЕ_ЗЕРНО / 20);
if ($ОБЕСПЕЧЕНО_ПИЩЕЙ < $НАСЕЛЕНИЕ){
$УМЕРЛО_ОТ_ГОЛОДА = $НАСЕЛЕНИЕ - $ОБЕСПЕЧЕНО_ПИЩЕЙ;
}
else {
$УМЕРЛО_ОТ_ГОЛОДА = 0;
}
goto Досрочное_увольнение if $УМЕРЛО_ОТ_ГОЛОДА > 0.45*$НАСЕЛЕНИЕ;
$СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 = (($ГОД - 1)*$СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 + $УМЕРЛО_ОТ_ГОЛОДА*100/$НАСЕЛЕНИЕ)/$ГОД;
$НАСЕЛЕНИЕ = $ОБЕСПЕЧЕНО_ПИЩЕЙ;
$УМЕРЛО_ОТ_ГОЛОДА_10 = $УМЕРЛО_ОТ_ГОЛОДА_10 + $УМЕРЛО_ОТ_ГОЛОДА;
$НАСЕЛЕНИЕ = $НАСЕЛЕНИЕ + $ПРИРОСТ_НАСЕЛЕНИЯ;
# 15% вероятность чумы
$ЧУМА = '';
if (rand() < 0.15){
$НАСЕЛЕНИЕ = int ($НАСЕЛЕНИЕ / 2);
$ЧУМА = "Ужасная чума ! Половина народу умерло.\n";
}
}

# =====================================

sub Годовой_отчет{
print "\n\nХаммурапи: прошу отчитаться.\n";
print "В ", $ГОД, " году \n", $УМЕРЛО_ОТ_ГОЛОДА, " человек умерло от голода.\n";
print "Прирост населения составил ", $ПРИРОСТ_НАСЕЛЕНИЯ," человек.\n";
print $ЧУМА;
print "Сейчас население составляет ", $НАСЕЛЕНИЕ, " человек\n";
print "Город владеет ", $ПЛОЩАДЬ, " акрами.\n";
print "Собрано зерна ", $Y, " бушелей с акра.\n";
print "Крысы съели ", $СЪЕДЕНО_КРЫСАМИ, " бушелей.\n";
print "Запасы зерна составляют ", $ЗЕРНО_В_АМБАРЕ, " бушелей.\n";
}

# =====================================

sub Итоги_десятилетия{
$L = $ПЛОЩАДЬ / $НАСЕЛЕНИЕ;
$C = sprintf "%6.2f", $СМЕРТНОСТЬ_ОТ_ГОЛОДА_10;
print "За время вашего 10 летнего пребывания на посту в среднем ", $C , " процентов\n";
print "населения ежегодно умирали от голода, в общей сложности ", $УМЕРЛО_ОТ_ГОЛОДА_10, " человек.\n";
print "Вы начали с 10 акров на человека, а закончили \n", $L; " акрами на человека.\n\n";

goto ЧУДОВИЩНЫЙ_РЕЗУЛЬТАТ if $СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 > 33;
goto ЧУДОВИЩНЫЙ_РЕЗУЛЬТАТ if $L < 7;

goto ПЛОХОЙ_РЕЗУЛЬТАТ if $СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 > 10;
goto ПЛОХОЙ_РЕЗУЛЬТАТ if $L < 9;

goto СРЕДНИЙ_РЕЗУЛЬТАТ if $СМЕРТНОСТЬ_ОТ_ГОЛОДА_10 > 3;
goto СРЕДНИЙ_РЕЗУЛЬТАТ if $L < 10;

#если площадь на человека >= 10 и смертность от голода <= 3%
goto ФАНТАСТИЧЕСКИЙ_РЕЗУЛЬТАТ;

ФАНТАСТИЧЕСКИЙ_РЕЗУЛЬТАТ:
print "Фантастический результат !!! Шарлемань, Дизраели и \nДжефферсон вместе не смогли бы сделать лучше ! \n";
goto ДО_СВИДАНИЯ;

ЧУДОВИЩНЫЙ_РЕЗУЛЬТАТ:
print "Из-за чрезвычайно плохого управления ты объявлен \"Предателем нации\" !!";
goto ДО_СВИДАНИЯ;

ПЛОХОЙ_РЕЗУЛЬТАТ:
print "Ваша тяжелая рука припахивает Нероном и Иваном IV. \nВыжившие люди считают вас плохим правителем и ужасно ненавидят ваши потроха ! \n";
goto ДО_СВИДАНИЯ;

СРЕДНИЙ_РЕЗУЛЬТАТ:
print "Ваши достижения могли бы быть лучше, но \nдела не совсем плохи. \n";
print int($НАСЕЛЕНИЕ*0.8*rand()), " человек хотели бы видеть вас убитым, но это обычная проблема для нас.\n";
goto ДО_СВИДАНИЯ;

ДО_СВИДАНИЯ:
}

# =====================================

exit;

Итоговые критерии весьма специфические - площадь земли на душу населения и смертность от голода. Поэтому чума здорово улучшает показатели - чем больше вымерло, тем джефферснее. А если "не повезло" - количество едоков не сокращается по естественным причинам - такая же смертность низведет управляющего в "иваны". Чисто невидимая рука рынка. Игра сочинена в какие-то дремучие года, и уже тогда либеральная общественность пренебрегала состоянием общества в целом, выпячивая "витрину". Игрокам с детства внушали: лишь бы права отдельной личности были защищены, а население нехай уменьшается, и территорию можно сдать. Шарлемань (Карл Великий) всегда так делал, не так ли ?
Previous post Next post
Up