Jump to content
  • Advertisement
Sign in to follow this  
  • entries
    292
  • comments
    557
  • views
    154100

Hammurabi

Sign in to follow this  
TANSTAAFL

241 views

My Hammurabi source and binaries

Yesterday was Memorial Day, and I didn't have anything to do, so I decided to dig into the game Hammurabi.

Original BASIC Source for Hammurabi

This game was written in line number BASIC, which is what I started out on those many years ago. ("those many years ago"=="18 years ago" and thanks for asking.)

I had to type in the BASIC source, which took around an hour or so.

But, of course, I have no interpreter for line number BASIC (and do not want one), so what is a programmer to do?

Naturally, I decided to port the game to C. I fired up VC++2005, made myself a main function, copied and pasted the BASIC source as comments into the main function, and set about the task of "translating" each line of BASIC into lines of C.

The result:

[SOURCE]
/*
Hammurabi Version 1.0.0 ("Faithful Port")
Copyright (C) 2006 Ernest S. Pazera

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Contact information:
ernestpazera@msn.com

"Original" game and source taken from:
BASIC Computer Games (Microcomputer Edition)
Edited by David H. Ahl
ISBN:0-89480-052-3

Version History:
1.0.0:
Initial Release
29MAY2006
The initial version is a "Faithful Port", which means that I did the very best I could
to duplicate the functionality of the original BASIC program in equivalent C code.
Variable names are as in the original code. Most of the functionality was easily replicable
with printf and scanf. The three GOSUBs had to be implemented as functions.
Natually, there are a lot of gotos, all of the variables are global,

*/

#include "stdio.h"
#include "conio.h"
#include "stdlib.h"

/*
This comes from a BASIC game,
so naturally all of the variables
are global and have esoteric names.
*/

int d1;//total deaths
int p1;//total starvation percentage
int z;//year number
int p;//current population
int s;//current number of bushels of grain
int h;//harvest
int e;//grain eaten by rats
int y;//grain per acre
int a;//acres
int i;//new population
int q;//input variable, grain fed to people
int d;//deaths, input variable
int c;//random numbers, fed population
int l;//acres per person
int n;//beep loop

//gosubs are best implemented as functions
void gosub710();//not enough grain
void gosub720();//not enough land
void gosub800();//generate a random number in c(1-5)

int main(int argc,char* argv[])
{
//10 PRINT TAB(32);"HAMURABI"
printf(" HAMURABI\n");
//20 PRINT TAB(15);"CREATIVE COMPUTING MORRISTOWN, NEW JERSEY"
printf(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
//30 PRINT:PRINT:PRINT
printf("\n\n\n");
//80 PRINT "TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA"
printf("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA\n");
//90 PRINT "FOR A TEN YEAR TERM OF OFFICE.":PRINT
printf("FOR A TEN YEAR TERM OF OFFICE.\n\n");
//95 D1=0:P1=0
d1=0;p1=0;
//100 Z=0:P=95:S=2800:H=3000:E-H-S
z=0;p=95;s=2800;h=3000;e=h-s;
//110 Y=3:A=H/Y:I=5:Q=1
y=3;a=h/y;i=5;q=1;

line210:
//210 D=0
d=0;

line215:
//215 PRINT:PRINT:PRINT "HAMURABI: I BEG TO REPORT TO YOU,":Z=Z+1
printf("\n\nHAMURABI: I BEG TO REPORT TO YOU,\n");z++;
//217 PRINT "IN YEAR";Z;",";D;"PEOPLE STARVED,";I;"CAME TO THE CITY,"
printf("IN YEAR %d, %d PEOPLE STARVED, %d CAME TO THE CITY,\n",z,d,i);
//218 P=P+I
p+=i;
//227 IF Q>0 THEN 230
if(q>0) goto line230;
//228 P=INT(P/2)
p/=2;
//229 PRINT "A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED."
printf("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.\n");

line230:
//230 PRINT "POPULATION IS NOW";P
printf("POPULATION IS NOW %d.\n",p);
//232 PRINT "THE CITY NOW OWNS";A;"ACRES."
printf("THE CITY NOW OWNS %d ACRES.\n",a);
//235 PRINT "YOU HARVESTED";Y;"BUSHELS PER ACRE."
printf("YOU HARVESTED %d BUSHELS PER ACRE.\n",y);
//250 PRINT "RATS ATE";E;"BUSHELS."
printf("RATS ATE %d BUSHELS.\n",e);
//260 PRINT "YOU HOW HAVE";S;"BUSHELS IN STORE.":PRINT
printf("YOU HOW HAVE %d BUSHELS IN STORE.\n\n",s);
//270 IF Z=11 THEN 860
if(z==11) goto line860;
//310 C=INT(10*RND(1)):Y=C+17
c=rand()%10;y=c+17;
//312 PRINT "LAND IS TRADING AT";Y;"BUSHELS PER ACRE."
printf("LAND IS TRADING AT %d BUSHELS PER ACRE.\n",y);

line320:
//320 PRINT "HOW MANY ACRES DO YOU WISH TO BUY";
printf("HOW MANY ACRES DO YOU WISH TO BUY?");
//321 INPUT Q:IF Q<0 THEN 850
scanf("%d",&q);if(q<0) goto line850;
//322 IF Y*Q
if((y*q)goto
line330;
//323 GOSUB 710
gosub710();
//324 GOTO 320
goto line320;

line330:
//330 IF Q=0 THEN 340
if(q==0) goto line340;
//331 A=A+Q:S=S-Y*Q:C=0
a+=q;s-=(y*q);c=0;
//334 GOTO 400
goto line400;

line340:
//340 PRINT "HOW MANY ACRES DO YOU WISH TO SELL";
printf("HOW MANY ACRES DO YOU WISH TO SELL?");
//341 INPUT Q:IF Q<0 THEN 850
scanf("%d",&q);if(q<0) goto line850;
//342 IF Q
if(qgoto
line350;
//343 GOSUB 720
gosub720();
//344 GOTO 340
goto line340;

line350:
//350 A=A-Q:S=S+Y*Q:C=0
a-=q;s+=(y*q);c=0;

line400:
//400 PRINT
printf("\n");

line410:
//410 PRINT "HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE";
printf("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE?");
//411 INPUT Q
scanf("%d",&q);
//412 IF Q<0 THEN 850
if(q<0) goto line850;
//418 REM *** TRYING TO USE MORE GRAIN THAN IS IN SILOS?
//420 IF Q<=S THEN 430
if(q<=s) goto line430;
//421 GOSUB 710
gosub710();
//422 GOTO 410
goto line410;

line430:
//430 S=S-Q:C=1:PRINT
s-=q;c=1;printf("\n");

line440:
//440 PRINT "HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED";
printf("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED?");
//441 INPUT D: IF D=0 THEN 511
scanf("%d",&d);
//442 IF D<0 THEN 850
if(d<0) goto line850;
//444 REM *** TRYING TO PLANT MORE ACRES THAN YOU OWN?
//445 IF D<=A THEN 450
if(d<=a) goto line450;
//446 GOSUB 720
gosub720();
//447 GOTO 440
goto line440;


//449 REM *** ENOUGH GRAIN FOR SEED?
line450:
//450 IF INT(D/2)<=S THEN 455
if((d/2)goto line455;
//452 GOSUB 710
gosub710();
//453 GOTO 440
goto line440;

//454 REM *** ENOUGH PEOPLE TO TEND THE CROPS?
line455:
//455 IF D<10*P THEN 510
//BUG: Obviously, 1 unit of population can work 10 acres.
//Change: Changed code to allow maximum efficiency.
if(d<=(10*p)) goto line510;
//460 PRINT "BUT YOU HAVE ONLY";P;"PEOPLE TO TEND THE FIELDS! NOW THEN,"
printf("BUT YOU HAVE ONLY %d PEOPLE TO TEND THE FIELDS! NOW THEN,\n",p);
//470 GOTO 440
goto line440;

line510:
//510 S=S-INT(D/2)
s-=(d>>1);
//511 GOSUB 800
gosub800();
//512 REM *** A BOUNTIFUL HARVEST!
//515 Y=C:H=D*Y:E=0
y=c;h=d*y;e=0;
//521 GOSUB 800
gosub800();
//522 IF INT(C/2)<>C/2 THEN 530
if((c&1)==1) goto line530;
//523 REM *** RATS ARE RUNNING WILD!!
//525 E=INT(S/2)
e=s>>1;

line530:
//530 S=S-E+H
s=s-e+h;
//531 GOSUB 800
gosub800();
//532 REM *** LET'S HAVE SOME BABIES
//533 I=INT(C*(20*A+S)/P/100+1)
i=(c*(20*a+s)/p/100)+1;
//539 REM *** HOW MANY PEOPLE HAD FULL TUMMIES?
//540 C=INT(Q/20)
c=q/20;
//541 REM *** HORROR, A 15% CHANCE OF PLAGUE
//542 Q=INT(10*(2*RND(1)-.3))
q=10*((2*(rand()%100)-30)/100);
//550 IF P
if(pgoto
line210;
//551 REM *** STARVE ENOUGH FOR IMPEACHMENT?
//552 D=P-C:IF D>.45*P THEN 560
d=p-c;if(d>(45*p/100)) goto line560;
//553 P1=((Z-1)*P1+D*100/P)/Z
p1=((z-1)*p1+d*100/p)/z;
//555 P=C:D1=D1+D:GOTO 215
p=c;d1+=d;goto line215;

line560:
//560 PRINT:PRINT "YOU STARVED";D;"PEOPLE IN ONE YEAR!!!"
printf("\nYOU STARVED %d PEOPLE IN ONE YEAR!!!\n",d);

line565:
//565 PRINT "DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY"
printf("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY\n");
//566 PRINT "BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE"
printf("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE\n");
//567 PRINT "ALSO BEEN DECLARED NATIONAL FINK!!!!":GOTO 990
printf("ALSO BEEN DECLARED NATIONAL FINK!!!!\n");goto line990;

line850:
//850 PRINT:PRINT "HAMURABI: I CANNOT DO WHAT YOU WISH."
printf("\nHAMURABI: I CANNOT DO WHAT YOU WISH.\n");
//855 PRINT "GET YOURSELF ANOTHER STEWARD!!!"
printf("GET YOURSELF ANOTHER STEWARD!!!\n");
//857 GOTO 990
goto line990;

line860:
//860 PRINT "IN YOUR 10-YEAR TERM OF OFFICE,";P1;"PERCENT OF THE"
printf("IN YOUR 10-YEAR TERM OF OFFICE, %d PERCENT OF THE\n",p1);
//862 PRINT "POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF"
printf("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF\n");
//865 PRINT D1;"PEOPLE DIED!!":L=A/P
printf("%d PEOPLE DIED!!\n",d1);l=a/p;
//870 PRINT "YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH"
printf("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH\n");
//875 PRINT L;"ACRES PER PERSON.":PRINT
printf("%d ACRES PER PERSON.\n\n",l);
//880 IF P1>33 THEN 565
if(p1>33) goto line565;
//885 IF L<7 THEN 565
if(l<7) goto line565;
//890 IF P1>10 THEN 940
if(p1>10) goto line940;
//892 IF L<9 THEN 940
if(l<9) goto line940;
//895 IF P1>3 THEN 960
if(p1>3) goto line960;
//896 IF L<10 THEN 960
if(l<10) goto line960;
//900 PRINT "A FANTASTIC PERFORMANCE!!! CHARLEMAGNE, DISRAELI, AND"
printf("A FANTASTIC PERFORMANCE!!! CHARLEMAGNE, DISRAELI, AND\n");
//905 PRINT "JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!":GOTO 990
printf("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n");goto line990;

line940:
//940 PRINT "YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVARN IV."
printf("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVARN IV.\n");
//945 PRINT "THE PEOPLE (REMAINING) FIND YOU AND UNPLEASANT RULE, AND,"
printf("THE PEOPLE (REMAINING) FIND YOU AND UNPLEASANT RULE, AND,\n");
//950 PRINT "FRANKLY, HATE YOUR GUTS!!":GOTO 990
printf("FRANKLY, HATE YOUR GUTS!!\n");goto line990;

line960:
//960 PRINT "YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT"
printf("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT\n");
//965 PRINT "REALLY WASN'T TOO BAD AT ALL. ";INT(P*.8(RND(1));"PEOPLE"
printf("REALLY WASN'T TOO BAD AT ALL. %d PEOPLE\n",rand()%((p*8)/10));
//970 PRINT "DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR"
printf("DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR\n");
//975 PRINT "TRIVIAL PROBLEMS."
printf("TRIVIAL PROBLEMS.\n");

line990:
//990 PRINT: FOR N=1 TO 10:PRINT CHR$(7);:NEXT N
printf("\n");for(n=1;n<=10;n++) printf("\x07");
//995 PRINT "SO LONG FOR NOW.":PRINT
printf("SO LONG FOR NOW.\n\n");
//999 END
return(0);
}

void gosub710()
{
//710 PRINT "HAMURABI: THINK AGAIN. YOU HAVE ONLY"
printf("HAMURABI: THINK AGAIN. YOU HAVE ONLY\n");
//711 PRINT S;"BUSHELS OF GRAIN. NOW THEN,"
printf("%d BUSHELS OF GRAIN. NOW THEN,\n",s);
//712 RETURN
}

void gosub720()
{
//720 PRINT "HAMURABI: THINK AGAIN. YOU OWN ONLY";A;"ACRES. NOW THEN,"
printf("HAMURABI: THINK AGAIN. YOU OWN ONLY %d ACRES. NOW THEN,\n",a);
//730 RETURN
}

void gosub800()
{
//800 C=INT(RND(1)*5)+1
c=(rand()%5)+1;
//801 RETURN
}
[/SOURCE]


It winds up approximately as ugly as the original BASIC source. Cryptic global variables, some of which are reused for entirely different purposed. There are gotos and gosubs, magic number constants, and all manner of evil ickiness. The labels and function calls are based on line number, and are completely useless descriptively speaking.

"I can do better."

[SOURCE]
/*
Hammurabi Version 1.0.1 ("Less Cryptic")
Copyright (C) 2006 Ernest S. Pazera

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Contact information:
ernestpazera@msn.com

"Original" game and source taken from:
BASIC Computer Games (Microcomputer Edition)
Edited by David H. Ahl
ISBN:0-89480-052-3

Version History:
1.0.1:
Less Cryptic
29MAY2006
Globals have been given descriptive names.
Functions have been given useful names.
Labels have been given useful names.
Recycled variables have been noted as overloaded, for later disambiguation.
Removed BASIC source from comments.
Added GameOverGood, GameTitle, GameInitialize.
Added gotos at the end of each game state, to facilitate moving the states into functions.
Now seeding random number generator during GameInitialize.
Some disambiquation flags for plague flag, price per acre and yield per acre.
Magic numbers have either vanished or been given TODO markers.
1.0.0:
Faithful Port
29MAY2006
The initial version is a "Faithful Port", which means that I did the very best I could
to duplicate the functionality of the original BASIC program in equivalent C code.
Variable names are as in the original code. Most of the functionality was easily replicable
with printf and scanf. The three GOSUBs had to be implemented as functions.
Natually, there are a lot of gotos, all of the variables are global,

*/

#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "time.h"

enum
{
INITIAL_POPULATION = 95,
INITIAL_POPULATION_INCREASE = 5,
INITIAL_BUSHELS = 2800,
INITIAL_HARVEST = 3000,
INITIAL_BUSHELS_YIELD_ACRE = 3,
INITIAL_YEAR = 0,
FINAL_YEAR = 10,
PRICE_PER_ACRE_MINIMUM = 17,
PRICE_PER_ACRE_MAXIMUM = 26,
ACRES_PER_BUSHEL_PLANTED = 2,
ACRES_PLANTED_PER_PERSON = 10,
BUSHELS_EATEN_PER_PERSON = 20,
IMPEACHMENT_PERCENTAGE = 45,
PERCENTAGE_STARVED_LOSE = 33,
PERCENTAGE_STARVED_BAD = 10,
PERCENTAGE_STARVED_MEDIOCRE = 3,
ACRES_PER_PERSON_LOSE = 7,
ACRES_PER_PERSON_BAD = 9,
ACRES_PER_PERSON_MEDIOCRE = 10,
ASSASSINATION_PERCENTAGE = 80
};

/*
This comes from a BASIC game,
so naturally all of the variables
are global and have esoteric names.
*/

int total_deaths;//total deaths
int overall_percentage_starved;//total starvation percentage
int current_year;//year number
int current_population;//current population
int current_bushels;//current number of bushels
int bushels_harvested;//harvest
int bushels_destroyed;//bushels eaten by rats
int bushels_per_acre;//bushels per acre **OVERLOADED**
int current_acres;//acres
int population_increase;//new population
int bushels_eaten;//acres purchased, acres sold, grain fed to people, plague flag **OVERLOADED**
int current_deaths;//deaths, acres planted **OVERLOADED**
int random_number;//random numbers, fed population **OVERLOADED**
int acres_per_person;//acres per person
int beep_count;//beep loop

//gosubs are best implemented as functions
void NotEnoughGrain();//not enough grain
void NotEnoughAcres();//not enough land
void GenerateRandomNumber();//generate a random number in random_number(1-5)

int main(int argc,char* argv[])
{
goto GameTitle;

GameTitle:
printf(" HAMURABI\n");
printf(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
printf("\n\n\n");
goto GameInitialize;

GameInitialize:
srand((unsigned int)time(0));
printf("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA\n");
printf("FOR A TEN YEAR TERM OF OFFICE.\n\n");
total_deaths=0;
overall_percentage_starved=0;
current_year=INITIAL_YEAR;
current_population=INITIAL_POPULATION;
current_bushels=INITIAL_BUSHELS;
bushels_harvested=INITIAL_HARVEST;
bushels_destroyed=bushels_harvested-current_bushels;
bushels_per_acre=INITIAL_BUSHELS_YIELD_ACRE;//TODO: yield, not price
current_acres=bushels_harvested/bushels_per_acre;//TODO: yield, not price
population_increase=INITIAL_POPULATION_INCREASE;
bushels_eaten=1;//TODO: this is the plague flag
goto NoDeaths;

NoDeaths:
current_deaths=0;
goto StartOfTurn;

StartOfTurn:
printf("\n\nHAMURABI: I BEG TO REPORT TO YOU,\n");current_year++;
printf("IN YEAR %d, %d PEOPLE STARVED, %d CAME TO THE CITY,\n",current_year,current_deaths,population_increase);
current_population+=population_increase;
if(bushels_eaten>0) goto TurnReport;//TODO: this is the plague flag
//TODO: fix it so that the effects of a plague are modifiable
current_population/=2;
printf("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.\n");
goto TurnReport;

TurnReport:
printf("POPULATION IS NOW %d.\n",current_population);
printf("THE CITY NOW OWNS %d ACRES.\n",current_acres);
printf("YOU HARVESTED %d BUSHELS PER ACRE.\n",bushels_per_acre);//TODO: yield, not price
printf("RATS ATE %d BUSHELS.\n",bushels_destroyed);
printf("YOU HOW HAVE %d BUSHELS IN STORE.\n\n",current_bushels);
if(current_year>FINAL_YEAR) goto GameOverDecideFate;
random_number=rand()%(PRICE_PER_ACRE_MAXIMUM-PRICE_PER_ACRE_MINIMUM+1);
bushels_per_acre=random_number+PRICE_PER_ACRE_MINIMUM;//TODO: this is price, not yield
printf("LAND IS TRADING AT %d BUSHELS PER ACRE.\n",bushels_per_acre);//TODO: price, not yield
goto PurchaseAcresPrompt;

PurchaseAcresPrompt:
printf("HOW MANY ACRES DO YOU WISH TO BUY?");
scanf("%d",&bushels_eaten);if(bushels_eaten<0) goto GameOverBadInput;
if((bushels_per_acre*bushels_eaten)goto PurchaseAcres;//TODO: price, not yield
NotEnoughGrain();
goto PurchaseAcresPrompt;

PurchaseAcres:
if(bushels_eaten==0) goto SellAcresPrompt;
current_acres+=bushels_eaten;
current_bushels-=(bushels_per_acre*bushels_eaten);//TODO: price, not yield
random_number=0;
goto DoneBuyingAndSelling;

SellAcresPrompt:
printf("HOW MANY ACRES DO YOU WISH TO SELL?");
scanf("%d",&bushels_eaten);
if(bushels_eaten<0) goto GameOverBadInput;
if(bushels_eatengoto SellAcres;
NotEnoughAcres();
goto SellAcresPrompt;

SellAcres:
current_acres-=bushels_eaten;
current_bushels+=(bushels_per_acre*bushels_eaten);//TODO: price, not yield
random_number=0;
goto DoneBuyingAndSelling;

DoneBuyingAndSelling:
printf("\n");
goto BushelsEatenPrompt;

BushelsEatenPrompt:
printf("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE?");
scanf("%d",&bushels_eaten);
if(bushels_eaten<0) goto GameOverBadInput;
//TRYING TO USE MORE GRAIN THAN IS IN SILOS?
if(bushels_eaten<=current_bushels) goto BushelsEaten;
NotEnoughGrain();
goto BushelsEatenPrompt;

BushelsEaten:
current_bushels-=bushels_eaten;
random_number=1;
printf("\n");
goto AcresPlanetedPrompt;

AcresPlanetedPrompt:
printf("HOW MANY ACRES DO YOU WISH TO PLANT WITH SEED?");
scanf("%d",¤t_deaths);
if(current_deaths<0) goto GameOverBadInput;
//TRYING TO PLANT MORE ACRES THAN YOU OWN?
if(current_deaths<=current_acres) goto EnoughBushelsForPlanting;
NotEnoughAcres();
goto AcresPlanetedPrompt;


EnoughBushelsForPlanting:
//ENOUGH GRAIN FOR SEED?
if((current_deaths/ACRES_PER_BUSHEL_PLANTED)goto EnoughPeopleForPlanting;
NotEnoughGrain();
goto AcresPlanetedPrompt;

EnoughPeopleForPlanting:
//ENOUGH PEOPLE TO TEND THE CROPS?
if(current_deaths<=(ACRES_PLANTED_PER_PERSON*current_population)) goto DoHarvest;
printf("BUT YOU HAVE ONLY %d PEOPLE TO TEND THE FIELDS! NOW THEN,\n",current_population);
goto AcresPlanetedPrompt;

DoHarvest:
current_bushels-=(current_deaths/ACRES_PER_BUSHEL_PLANTED);
GenerateRandomNumber();
//A BOUNTIFUL HARVEST!
bushels_per_acre=random_number;//TODO: yield, not price
bushels_harvested=current_deaths*bushels_per_acre;//TODO: yield, not price
bushels_destroyed=0;
GenerateRandomNumber();
//TODO: a better way of making the rats happen 40% of the time
if((random_number&1)==1) goto EndOfTurn;
//RATS ARE RUNNING WILD!!
//TODO: a better way to customize how much of the food the rats will eat
bushels_destroyed=current_bushels/2;
goto EndOfTurn;

EndOfTurn:
current_bushels=current_bushels-bushels_destroyed+bushels_harvested;
GenerateRandomNumber();
//LET'S HAVE SOME BABIES
population_increase=(random_number*(20*current_acres+current_bushels)/current_population/100)+1;//TODO: this formula is too complicated
//HOW MANY PEOPLE HAD FULL TUMMIES?
random_number=bushels_eaten/BUSHELS_EATEN_PER_PERSON;
//HORROR, A 15% CHANCE OF PLAGUE
bushels_eaten=10*((2*(rand()%100)-30)/100);//TODO: this is the plague flag, and this formula is too complicated
if(current_populationgoto NoDeaths;
//STARVE ENOUGH FOR IMPEACHMENT?
current_deaths=current_population-random_number;
if(current_deaths>(IMPEACHMENT_PERCENTAGE*current_population/100)) goto Impeachment;
overall_percentage_starved=((current_year-INITIAL_YEAR-1)*overall_percentage_starved+current_deaths*100/current_population)/(current_year-INITIAL_YEAR);//TODO: this formula is too complicated
current_population=random_number;
total_deaths+=current_deaths;
goto StartOfTurn;

Impeachment:
printf("\nYOU STARVED %d PEOPLE IN ONE YEAR!!!\n",current_deaths);
goto GameOverLose;

GameOverLose:
printf("DUE TO THIS EXTREME MISMANAGEMENT YOU HAVE NOT ONLY\n");
printf("BEEN IMPEACHED AND THROWN OUT OF OFFICE BUT YOU HAVE\n");
printf("ALSO BEEN DECLARED NATIONAL FINK!!!!\n");
goto ExitGame;

GameOverBadInput:
printf("\nHAMURABI: I CANNOT DO WHAT YOU WISH.\n");
printf("GET YOURSELF ANOTHER STEWARD!!!\n");
goto ExitGame;

GameOverDecideFate:
printf("IN YOUR 10-YEAR TERM OF OFFICE, %d PERCENT OF THE\n",overall_percentage_starved);
printf("POPULATION STARVED PER YEAR ON THE AVERAGE, I.E. A TOTAL OF\n");
printf("%d PEOPLE DIED!!\n",total_deaths);
acres_per_person=current_acres/current_population;
printf("YOU STARTED WITH 10 ACRES PER PERSON AND ENDED WITH\n");
printf("%d ACRES PER PERSON.\n\n",acres_per_person);
if(overall_percentage_starved>PERCENTAGE_STARVED_LOSE) goto GameOverLose;
if(acres_per_persongoto GameOverLose;
if(overall_percentage_starved>PERCENTAGE_STARVED_BAD) goto GameOverBad;
if(acres_per_persongoto GameOverBad;
if(overall_percentage_starved>PERCENTAGE_STARVED_MEDIOCRE) goto GameOverMediocre;
if(acres_per_persongoto GameOverMediocre;
goto GameOverGood;

GameOverGood:
printf("A FANTASTIC PERFORMANCE!!! CHARLEMAGNE, DISRAELI, AND\n");
printf("JEFFERSON COMBINED COULD NOT HAVE DONE BETTER!\n");
goto ExitGame;

GameOverBad:
printf("YOUR HEAVY-HANDED PERFORMANCE SMACKS OF NERO AND IVARN IV.\n");
printf("THE PEOPLE (REMAINING) FIND YOU AN UNPLEASANT RULER AND,\n");
printf("FRANKLY, HATE YOUR GUTS!!\n");
goto ExitGame;

GameOverMediocre:
printf("YOUR PERFORMANCE COULD HAVE BEEN SOMEWHAT BETTER, BUT\n");
printf("REALLY WASN'T TOO BAD AT ALL. %d PEOPLE\n",rand()%((current_population*ASSASSINATION_PERCENTAGE)/100));//TODO: this calculation is too complex
printf("DEARLY LIKE TO SEE YOU ASSASSINATED BUT WE ALL HAVE OUR\n");
printf("TRIVIAL PROBLEMS.\n");
goto ExitGame;

ExitGame:
printf("\n");for(beep_count=1;beep_count<=10;beep_count++) printf("\x07");
printf("SO LONG FOR NOW.\n\n");

return(0);
}

void NotEnoughGrain()
{
printf("HAMURABI: THINK AGAIN. YOU HAVE ONLY\n");
printf("%d BUSHELS OF GRAIN. NOW THEN,\n",current_bushels);
}

void NotEnoughAcres()
{
printf("HAMURABI: THINK AGAIN. YOU OWN ONLY %d ACRES. NOW THEN,\n",current_acres);
}

void GenerateRandomNumber()
{
random_number=(rand()%5)+1;
}
[/SOURCE]


Still a lot of gotos, but at least the names of things are more meaningful. You can see a couple of TODO lines as I went through here.

"Still, I can do better."

[SOURCE]
/*
Hammurabi Version 1.0.2 ("")
Copyright (C) 2006 Ernest S. Pazera

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA

Contact information:
ernestpazera@msn.com

"Original" game and source taken from:
BASIC Computer Games (Microcomputer Edition)
Edited by David H. Ahl
ISBN:0-89480-052-3

Version History:
1.0.2:
29MAY2006
Switched from gotos to game states.
Disambiguated variables.
Scoped variables locally where possible.
Moved to C++, but only for using enumerations without first specifying the keyword 'enum'.
1.0.1:
Less Cryptic
29MAY2006
Globals have been given descriptive names.
Functions have been given useful names.
Labels have been given useful names.
Recycled variables have been noted as overloaded, for later disambiguation.
Removed BASIC source from comments.
Added GameOverGood, GameTitle, GameInitialize.
Added gotos at the end of each game state, to facilitate moving the states into functions.
Now seeding random number generator during GameInitialize.
Some disambiquation flags for plague flag, price per acre and yield per acre.
Magic numbers have either vanished or been given TODO markers.
1.0.0:
Faithful Port
29MAY2006
The initial version is a "Faithful Port", which means that I did the very best I could
to duplicate the functionality of the original BASIC program in equivalent C code.
Variable names are as in the original code. Most of the functionality was easily replicable
with printf and scanf. The three GOSUBs had to be implemented as functions.
Natually, there are a lot of gotos, all of the variables are global,

*/

#include "stdio.h"
#include "conio.h"
#include "stdlib.h"
#include "time.h"

enum state_t
{
STATE_GAMETITLE,
STATE_GAMEINITIALIZE,
STATE_NODEATHS,
STATE_STARTOFTURN,
STATE_TURNREPORT,
STATE_PURCHASEACRESPROMPT,
STATE_PURCHASEACRES,
STATE_SELLACRESPROMPT,
STATE_SELLACRES,
STATE_DONEBUYINGANDSELLING,
STATE_BUSHELSEATENPROMPT,
STATE_BUSHELSEATEN,
STATE_ACRESPLANTEDPROMPT,
STATE_ENOUGHBUSHELSFORPLANTING,
STATE_ENOUGHPEOPLEFORPLANTING,
STATE_DOHARVEST,
STATE_ENDOFTURN,
STATE_IMPEACHMENT,
STATE_GAMEOVERLOSE,
STATE_GAMEOVERBADINPUT,
STATE_GAMEOVERDECIDEFATE,
STATE_GAMEOVERGOOD,
STATE_GAMEOVERBAD,
STATE_GAMEOVERMEDIOCRE,
STATE_EXITGAME,

STATE_COUNT
};

enum
{
INITIAL_POPULATION = 95,
INITIAL_POPULATION_INCREASE = 5,
INITIAL_BUSHELS = 2800,
INITIAL_HARVEST = 3000,
INITIAL_BUSHELS_YIELD_ACRE = 3,
INITIAL_YEAR = 0,
FINAL_YEAR = 10,
PRICE_PER_ACRE_MINIMUM = 17,
PRICE_PER_ACRE_MAXIMUM = 26,
ACRES_PER_BUSHEL_PLANTED = 2,
ACRES_PLANTED_PER_PERSON = 10,
BUSHELS_EATEN_PER_PERSON = 20,
IMPEACHMENT_PERCENTAGE = 45,
PERCENTAGE_STARVED_LOSE = 33,
PERCENTAGE_STARVED_BAD = 10,
PERCENTAGE_STARVED_MEDIOCRE = 3,
ACRES_PER_PERSON_LOSE = 7,
ACRES_PER_PERSON_BAD = 9,
ACRES_PER_PERSON_MEDIOCRE = 10,
ASSASSINATION_PERCENTAGE = 80,
BUSHEL_YIELD_PER_ACRE_MINIMUM = 1,
BUSHEL_YIELD_PER_ACRE_MAXIMUM = 5,
PERCENT_CHANCE_RATS = 40,
PERCENT_CHANCE_PLAGUE = 15
};

/*
This comes from a BASIC game,
so naturally all of the variables
are global and have esoteric names.
*/

int total_deaths;//total deaths
int overall_percentage_starved;//total starvation percentage
int current_year;//year number
int current_population;//current population
int current_bushels;//current number of bushels
int bushels_harvested;//harvest
int bushels_destroyed;//bushels eaten by rats
int bushels_yield_per_acre;//bushels yield per acre
int bushels_price_per_acre;//bushels price per acre
int current_acres;//acres
int population_increase;//new population
int bushels_eaten;//grain fed to people
int acres_purchased;//acres purchased
int acres_sold;//acres sold
int current_deaths;//deaths
int acres_planted;//acres planted
state_t state;//game state

void NotEnoughGrain();
void NotEnoughAcres();
int GenerateRandomNumber(int minimum,int maximum);

//game state functions
state_t GameTitle();
state_t GameInitialize();
state_t NoDeaths();
state_t StartOfTurn();
state_t TurnReport();
state_t PurchaseAcresPrompt();
state_t PurchaseAcres();
state_t SellAcresPrompt();
state_t SellAcres();
state_t DoneBuyingAndSelling();
state_t BushelsEatenPrompt();
state_t BushelsEaten();
state_t AcresPlantedPrompt();
state_t EnoughBushelsForPlanting();
state_t EnoughPeopleForPlanting();
state_t DoHarvest();
state_t EndOfTurn();
state_t Impeachment();
state_t GameOverLose();
state_t GameOverBadInput();
state_t GameOverDecideFate();
state_t GameOverGood();
state_t GameOverBad();
state_t GameOverMediocre();
state_t ExitGame();


int main(int argc,char* argv[])
{
for(state = STATE_GAMETITLE;state!=STATE_COUNT;)
{
switch(state)
{
case STATE_GAMETITLE:
{
state = GameTitle();
}break;
case STATE_GAMEINITIALIZE:
{
state = GameInitialize();
}break;
case STATE_NODEATHS:
{
state = NoDeaths();
}break;
case STATE_STARTOFTURN:
{
state = StartOfTurn();
}break;
case STATE_TURNREPORT:
{
state = TurnReport();
}break;
case STATE_PURCHASEACRESPROMPT:
{
state = PurchaseAcresPrompt();
}break;
case STATE_PURCHASEACRES:
{
state = PurchaseAcres();
}break;
case STATE_SELLACRESPROMPT:
{
state = SellAcresPrompt();
}break;
case STATE_SELLACRES:
{
state = SellAcres();
}break;
case STATE_DONEBUYINGANDSELLING:
{
state = DoneBuyingAndSelling();
}break;
case STATE_BUSHELSEATENPROMPT:
{
state = BushelsEatenPrompt();
}break;
case STATE_BUSHELSEATEN:
{
state = BushelsEaten();
}break;
case STATE_ACRESPLANTEDPROMPT:
{
state = AcresPlantedPrompt();
}break;
case STATE_ENOUGHBUSHELSFORPLANTING:
{
state = EnoughBushelsForPlanting();
}break;
case STATE_ENOUGHPEOPLEFORPLANTING:
{
state = EnoughPeopleForPlanting();
}break;
case STATE_DOHARVEST:
{
state = DoHarvest();
}break;
case STATE_ENDOFTURN:
{
state = EndOfTurn();
}break;
case STATE_IMPEACHMENT:
{
state = Impeachment();
}break;
case STATE_GAMEOVERLOSE:
{
state = GameOverLose();
}break;
case STATE_GAMEOVERBADINPUT:
{
state = GameOverBadInput();
}break;
case STATE_GAMEOVERDECIDEFATE:
{
state = GameOverDecideFate();
}break;
case STATE_GAMEOVERGOOD:
{
state = GameOverGood();
}break;
case STATE_GAMEOVERBAD:
{
state = GameOverBad();
}break;
case STATE_GAMEOVERMEDIOCRE:
{
state = GameOverMediocre();
}break;
case STATE_EXITGAME:
{
state = ExitGame();
}break;
default:
{
state=STATE_COUNT;
}break;
}
}

return(0);
}

void NotEnoughGrain()
{
printf("HAMURABI: THINK AGAIN. YOU HAVE ONLY\n");
printf("%d BUSHELS OF GRAIN. NOW THEN,\n",current_bushels);
}

void NotEnoughAcres()
{
printf("HAMURABI: THINK AGAIN. YOU OWN ONLY %d ACRES. NOW THEN,\n",current_acres);
}

int GenerateRandomNumber(int minimum,int maximum)
{
return((rand()%(maximum-minimum+1))+minimum);
}

state_t GameTitle()//
{
printf(" HAMURABI\n");
printf(" CREATIVE COMPUTING MORRISTOWN, NEW JERSEY\n");
printf("\n\n\n");
return(STATE_GAMEINITIALIZE);
}

state_t GameInitialize()
{
srand((unsigned int)time(0));
printf("TRY YOUR HAND AT GOVERNING ANCIENT SUMERIA\n");
printf("FOR A TEN YEAR TERM OF OFFICE.\n\n");
total_deaths=0;
overall_percentage_starved=0;
current_year=INITIAL_YEAR;
current_population=INITIAL_POPULATION;
current_bushels=INITIAL_BUSHELS;
bushels_harvested=INITIAL_HARVEST;
bushels_destroyed=bushels_harvested-current_bushels;
bushels_yield_per_acre=INITIAL_BUSHELS_YIELD_ACRE;
current_acres=bushels_harvested/bushels_yield_per_acre;
population_increase=INITIAL_POPULATION_INCREASE;
return(STATE_NODEATHS);
}

state_t NoDeaths()
{
current_deaths=0;
return(STATE_STARTOFTURN);
}

state_t StartOfTurn()
{
bool plague_flag=false;
if(current_year>INITIAL_YEAR)
{
plague_flag=GenerateRandomNumber(1,100)<=PERCENT_CHANCE_PLAGUE;
}
printf("\n\nHAMURABI: I BEG TO REPORT TO YOU,\n");
current_year++;
printf("IN YEAR %d, %d PEOPLE STARVED, %d CAME TO THE CITY,\n",current_year,current_deaths,population_increase);
current_population+=population_increase;
if(!plague_flag) return(STATE_TURNREPORT);
//TODO: fix it so that the effects of a plague are modifiable
current_population/=2;
printf("A HORRIBLE PLAGUE STRUCK! HALF THE PEOPLE DIED.\n");
return(STATE_TURNREPORT);
}

state_t TurnReport()
{
printf("POPULATION IS NOW %d.\n",current_population);
printf("THE CITY NOW OWNS %d ACRES.\n",current_acres);
printf("YOU HARVESTED %d BUSHELS PER ACRE.\n",bushels_yield_per_acre);
printf("RATS ATE %d BUSHELS.\n",bushels_destroyed);
printf("YOU HOW HAVE %d BUSHELS IN STORE.\n\n",current_bushels);
if(current_year>FINAL_YEAR) return(STATE_GAMEOVERDECIDEFATE);
bushels_price_per_acre=GenerateRandomNumber(PRICE_PER_ACRE_MINIMUM,PRICE_PER_ACRE_MAXIMUM);
printf("LAND IS TRADING AT %d BUSHELS PER ACRE.\n",bushels_price_per_acre);
return(STATE_PURCHASEACRESPROMPT);
}

state_t PurchaseAcresPrompt()
{
printf("HOW MANY ACRES DO YOU WISH TO BUY?");
scanf("%d",&acres_purchased);if(acres_purchased<0) return(STATE_GAMEOVERBADINPUT);
if((bushels_price_per_acre*acres_purchased)return(STATE_PURCHASEACRES);
NotEnoughGrain();
return(STATE_PURCHASEACRESPROMPT);
}

state_t PurchaseAcres()
{
if(acres_purchased==0) return(STATE_SELLACRESPROMPT);
current_acres+=acres_purchased;
current_bushels-=(bushels_price_per_acre*acres_purchased);
return(STATE_DONEBUYINGANDSELLING);
}

state_t SellAcresPrompt()
{
printf("HOW MANY ACRES DO YOU WISH TO SELL?");
scanf("%d",&acres_sold);
if(acres_sold<0) return(STATE_GAMEOVERBADINPUT);
if(acres_soldreturn(STATE_SELLACRES);
NotEnoughAcres();
return(STATE_SELLACRESPROMPT);
}

state_t SellAcres()
{
current_acres-=acres_sold;
current_bushels+=(bushels_price_per_acre*acres_sold);
return(STATE_DONEBUYINGANDSELLING);
}

state_t DoneBuyingAndSelling()
{
printf("\n");
return(STATE_BUSHELSEATENPROMPT);
}

state_t BushelsEatenPrompt()
{
printf("HOW MANY BUSHELS DO YOU WISH TO FEED YOUR PEOPLE?");
scanf("%
Sign in to follow this  


1 Comment


Recommended Comments

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!