hammurabi-p9/hammurabi.c

547 lines
17 KiB
C
Raw Normal View History

2020-06-14 06:04:08 +00:00
/*
* hammurabi - ancient Sumerian city-state resource management game
*
* Copyright 2020 David Meyer <papa@sdf.org> +JMJ
*
*/
2020-06-21 14:52:06 +00:00
/*
Hammurabi To-Dos
6/21
v Complete splash message: version, author, one-liner
- Write man page (for TWENEX release?)
- Complete/format help text: add introduction, credit to Ahl, separate intro and help
- Add help choice to annual plan menu
v Divider line is off-center
- Reformat menu to stand out -- plan summary should include grain and land balances
v BUG: buying land resulted in REDUCTION of land held
- Refactor and document main loop
/}_ _
\/\/
|\
|\
|_\_
2020-06-30 15:06:22 +00:00
____________________
( @
| |
| |
| |
|___________________|
(___________________@
2020-06-21 14:52:06 +00:00
*/
2020-06-14 06:04:08 +00:00
#include <u.h>
#include <libc.h>
#include <stdio.h>
2020-06-19 16:19:33 +00:00
/* Constants */
2020-06-14 06:04:08 +00:00
#define MAXYEARS 10
#define INITPOP 100
#define INITGRAIN 2800
#define INITLAND 1000
2020-06-19 16:19:33 +00:00
/* Types */
2020-06-14 06:04:08 +00:00
typedef enum {FALSE, TRUE} Boolean;
2020-06-15 14:24:12 +00:00
typedef enum {BUY, SELL, FEED, PLANT, RESET, EXEC, CALC, QUIT} Plancmd;
2020-06-14 06:04:08 +00:00
2020-06-19 16:19:33 +00:00
/* Message texts */
2020-06-14 06:04:08 +00:00
char MSGDIV[] =
2020-06-21 14:52:06 +00:00
" ----------------- <<<<>>>> --------------------\n";
2020-06-14 06:04:08 +00:00
char MSGHELP[] =
"Help text here\n\
Land price varies between 17 and 26 bushels of grain per acre.\n\
Planting two acres requires one bushel of grain.\n\
One person can farm ten acres of land.\n\
One person needs to eat 20 bushels of grain per year to stay healthy.\n\
";
2020-06-19 16:19:33 +00:00
char MSGMENU[]=
2020-07-01 14:56:44 +00:00
"\n\
[F]eed your people with grain | [E]xecute your current plan\n\
[P]lant crops on your land | [R]eset your current plan and start over\n\
[B]uy more land | [Q]uit the game\n\
2020-06-19 16:19:33 +00:00
";
2020-07-01 14:56:44 +00:00
char MSGPLTOT[] =
" ------------------------------ | ------------------------------\n";
2020-06-14 06:04:08 +00:00
char MSGSPLASH[] =
" ___ ___ ___. .__ \n\
/ | \\_____ _____ _____ __ ______________ \\_ |__ |__|\n\
/ ~ \\__ \\ / \\ / \\| | \\_ __ \\__ \\ | __ \\| |\n\
\\ Y // __ \\| Y Y \\ Y Y \\ | /| | \\// __ \\| \\_\\ \\ |\n\
\\___|_ /(____ /__|_| /__|_| /____/ |__| (____ /___ /__|\n\
\\/ \\/ \\/ \\/ \\/ \\/ \n\
2020-06-21 14:52:06 +00:00
The ancient game of ,.__., resource management\n\
2020-06-14 06:04:08 +00:00
/,',.`.\\\n\
2020-06-21 14:52:06 +00:00
Version 1.0 ____|:|db|:|____ by David Meyer\n\
for Plan 9 ___//\"\"\"|:|88|:|\"\"\"\\\\___ <papa@sdf.org>\n\
2020-06-14 06:04:08 +00:00
_____//\":=_=_=_=|--|=_=_=_=:\"\\\\_____\n\
__/.-._//__|L_L_L_L|--|L_L_L_L|__\\\\_.-.\\__\n\
//\"||m|:.-. .-. .-.,.__.,.-. .-. .-.:|m||\"\\\\\n\
// ||8||: : L_L_L_/.',.`.\\_L_L_L : :||8|| \\\\\n\
/'=======\"L.='.::'`:|:|db|:|:'`::.`=.L\"=======`\\\n\
| .---. .--. |_:==' |:|88|:| `==:_| .--. .---. |\n\
| | : |_.='/=======|:--:|=======\\`=._| : | |\n\
| | _,=' ||==.=.==|:--:|==.=.==|| `=._ | |\n\
|_:='_______|| || || |:--:| || || ||_______`=:_|\n\
|| || || |:--:| || || ||\n\
''-------|:--:|-------''\n\
':==:'\n\
";
2020-06-19 16:19:33 +00:00
/* Honorifics */
2020-06-14 06:04:08 +00:00
#define MAXHONOR 9
char *HONORIFIC[] = {"Great", "Mighty", "Wise", "Benevolent", "Merciful", "Just", "Sublime",
"Glorious", "Serene", "Majestic"};
2020-06-19 16:19:33 +00:00
/* Function prototypes */
2020-06-15 14:24:12 +00:00
void completedterm(void);
2020-06-19 16:19:33 +00:00
int inputbuy(void);
int inputfood(void);
2020-06-15 14:24:12 +00:00
int inputiexpr(void);
2020-06-19 16:19:33 +00:00
int inputplant(void);
int inputsell(void);
void microcalc(void);
Plancmd planmenu(void);
2020-06-14 06:04:08 +00:00
Boolean planyear(void);
2020-06-15 14:24:12 +00:00
void printdeposed(void);
2020-06-14 06:04:08 +00:00
void printgreeting(void);
void printlandprice(void);
void printstatus(void);
void printyearrept(void);
Boolean scanyes(char *prompt);
void setlandprice(void);
2020-06-19 16:19:33 +00:00
/* Constant variables */
int CONSCTL;
/* Game state */
2020-06-14 06:04:08 +00:00
int Year = 0;
2020-06-19 16:19:33 +00:00
int Pop = INITPOP;
2020-06-14 06:04:08 +00:00
int Grain = INITGRAIN;
int Land = INITLAND;
2020-06-19 16:19:33 +00:00
int StarvedTot = 0;
int StarvedPctTot = 0;
2020-06-14 06:04:08 +00:00
2020-06-19 16:19:33 +00:00
/* Annual plan */
2020-06-14 06:04:08 +00:00
int foodgrain, landsale, plantland;
2020-06-19 16:19:33 +00:00
/* Annual events */
2020-06-15 14:24:12 +00:00
int harvyield, landprice, newpop, plaguedeath, popfed, ratgrain, starvedeath, starvepct;
2020-06-14 06:04:08 +00:00
2020-06-15 14:24:12 +00:00
Boolean terminated = FALSE;
2020-06-14 06:04:08 +00:00
void
main(void)
{
2020-06-19 16:19:33 +00:00
CONSCTL = open("/dev/consctl", OWRITE);
if(CONSCTL == -1) exits("can't open CONSCTL: %r\n");
2020-06-14 06:04:08 +00:00
srand(time(0));
print(MSGSPLASH);
if(scanyes("Would you like instructions?") == TRUE) print(MSGHELP);
printgreeting();
printstatus();
setlandprice();
printlandprice();
2020-06-15 14:24:12 +00:00
do
2020-06-14 06:04:08 +00:00
{
2020-06-15 14:24:12 +00:00
++ Year;
if(planyear() == FALSE) terminated = TRUE;
else
2020-06-14 06:04:08 +00:00
{
Grain = Grain + (landsale * landprice) - (plantland / 2) - foodgrain;
Land = Land - landsale;
harvyield = nrand(5) + 1;
switch(nrand(5)+1)
{
case 1:
ratgrain = Grain;
break;
case 3:
ratgrain = Grain / 3;
break;
case 5:
ratgrain = Grain / 5;
break;
default:
ratgrain = 0;
}
Grain = Grain + (plantland * harvyield) - ratgrain;
2020-06-19 16:19:33 +00:00
newpop = (nrand(5) + 1) * (20 * Land + Grain) / Pop / 100 + 1;
2020-06-14 06:04:08 +00:00
popfed = foodgrain / 20;
2020-06-19 16:19:33 +00:00
starvedeath = ((popfed < Pop) ? (Pop - popfed) : 0);
StarvedTot = StarvedTot + starvedeath;
starvepct = 100 * starvedeath / Pop;
2020-06-15 14:24:12 +00:00
if(starvepct > 45)
{
printgreeting();
print("You have starved %d of your subjects in one year!\n", starvedeath);
printdeposed();
terminated = TRUE;
}
else
{
2020-06-19 16:19:33 +00:00
StarvedPctTot = StarvedPctTot + starvepct;
Pop = Pop + newpop - starvedeath;
plaguedeath = ((nrand(100) < 15) ? (Pop / 2) : 0);
Pop = Pop - plaguedeath;
2020-06-15 14:24:12 +00:00
printgreeting();
printyearrept();
2020-06-21 14:52:06 +00:00
printstatus();
2020-06-14 06:04:08 +00:00
2020-06-15 14:24:12 +00:00
if(Year == MAXYEARS) completedterm();
2020-06-21 14:52:06 +00:00
else
{
setlandprice();
printlandprice();
}
2020-06-15 14:24:12 +00:00
}
2020-06-14 06:04:08 +00:00
}
2020-06-15 14:24:12 +00:00
} while(Year < MAXYEARS && terminated == FALSE);
print(MSGDIV);
2020-06-14 06:04:08 +00:00
exits(0);
}
2020-06-15 14:24:12 +00:00
void
completedterm(void)
{
2020-06-19 16:19:33 +00:00
int avgstarvepct = StarvedPctTot / MAXYEARS;
int landperperson = Land / Pop;
int likeassassination = Pop * 0.8 * frand();
2020-06-15 14:24:12 +00:00
printgreeting();
2020-06-19 16:19:33 +00:00
print("In your %d-year reign, %d people died of starvation, an average of %d people per year.\n", MAXYEARS, StarvedTot, avgstarvepct);
2020-06-15 14:24:12 +00:00
print("You started with %d acres per person and ended with %d acres per person.\n", INITLAND / INITPOP, landperperson);
if(avgstarvepct > 33 || landperperson < 7) printdeposed();
2020-06-21 14:52:06 +00:00
else if(avgstarvepct > 10 || landperperson < 9) print("Your heavy-handed performance smacks of Nero and Ivan IV. Your (surviving) people find you an unpleasant ruler, and, frankly, hate your guts!\n");
2020-06-15 14:24:12 +00:00
else if(avgstarvepct > 3 || landperperson < 10)
{
print("Your performance could have been better, but wasn't too bad. ");
if(likeassassination > 0) print("%d %s would like to see you assassinated, but we all have our trivial problems.\n", likeassassination, (likeassassination == 1 ? "person" : "people"));
}
2020-06-21 14:52:06 +00:00
else print("A fantastic performance! Charlemagne, Disraeli, and Jefferson combined could not have done better!\n");
2020-06-15 14:24:12 +00:00
}
2020-06-19 16:19:33 +00:00
int
inputbuy(void)
{
int buy, grainbal, price;
Boolean valid = FALSE;
grainbal = Grain - foodgrain - (plantland / 2);
do
{
print("The price of land is %d bushels per acre.\n", landprice);
print("How many acres do you wish to buy? ");
buy = inputiexpr();
price = buy * landprice;
if(buy < 0)
print("Sire, it is impossible to buy a negative amount of land.\nIf you wish to sell land, enter \"0\", then select (S)ell from the menu.\n");
else if(price > grainbal)
print("Sire, %d acres of land costs %d bushels of grain, and you have only %d bushels.\n",
buy, price, grainbal);
else valid = TRUE;
} while(valid == FALSE);
return buy;
}
int
inputfood(void)
{
int food, grainbal;
Boolean valid = FALSE;
grainbal = Grain + (landsale * landprice) - (plantland / 2);
do
{
print("How much grain do you wish to give your people for food? ");
food = inputiexpr();
if(food < 0)
print("Sire, it is impossible to give the people a negative amount of grain.\nEnter \"0\" to return to the menu.\n");
else if(food > grainbal)
print("Sire, you only have %d bushels of grain in the granaries.\n", grainbal);
else valid = TRUE;
} while(valid == FALSE);
return food;
}
2020-06-15 14:24:12 +00:00
int
inputiexpr(void)
{
char buf[80];
int buflen = 80;
int result = 0;
int ch, rix, wix, p, o, n[40];
char op[39];
if(fgets(buf, buflen, stdin) != NULL)
{
if(buf[strlen(buf) - 1] != '\n') while((ch = getchar()) != '\n' && ch != EOF) ;
rix = wix = 0;
while(buf[rix] != '\0')
{
if(strchr("0123456789+-*/", buf[rix]) != 0)
{
if(rix > wix) buf[wix] = buf[rix];
++wix;
}
++ rix;
}
buf[wix] = '\0';
p = sscanf(buf, "%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d%c%d",
&n[0],&op[0],&n[1],&op[1],&n[2],&op[2],&n[3],&op[3],&n[4],&op[4],&n[5],&op[5],&n[6],&op[6],&n[7],&op[7],&n[8],&op[8],&n[9],&op[9],
&n[10],&op[10],&n[11],&op[11],&n[12],&op[12],&n[13],&op[13],&n[14],&op[14],&n[15],&op[15],&n[16],&op[16],&n[17],&op[17],&n[18],&op[18],&n[19],&op[19],
&n[20],&op[20],&n[21],&op[21],&n[22],&op[22],&n[23],&op[23],&n[24],&op[24],&n[25],&op[25],&n[26],&op[26],&n[27],&op[27],&n[28],&op[28],&n[29],&op[29],
&n[30],&op[30],&n[31],&op[31],&n[32],&op[32],&n[33],&op[33],&n[34],&op[34],&n[35],&op[35],&n[36],&op[36],&n[37],&op[37],&n[38],&op[38],&n[39]);
if(p > 0)
{
result = n[0];
for(o = 0; o < (int) (p / 2); ++ o)
{
switch (op[o])
{
case '+':
result = result + n[o+1];
break;
case '-':
result = result - n[o+1];
break;
case '*':
result = result * n[o+1];
break;
case '/':
result = result / n[o+1];
break;
}
}
}
}
return result;
}
2020-06-19 16:19:33 +00:00
int
inputplant(void)
{
int grainbal, landbal, plant;
Boolean valid = FALSE;
grainbal = Grain - foodgrain + (landsale * landprice);
2020-06-21 14:52:06 +00:00
landbal = Land - landsale;
2020-06-19 16:19:33 +00:00
do
{
print("How many acres do you wish to plant? ");
plant = inputiexpr();
if(plant < 0)
print("Sire, it is impossible to plant a negative amount of land.\nEnter \"0\" to return to the menu.\n");
else if(plant > landbal) print("Sire, you have only %d acres of lands.\n", landbal);
else if((plant / 2) > grainbal)
print("Sire, you have only %d bushels of grain in the granaries.\n", grainbal);
else if(plant > (10 * Pop)) print("Sire, you have only enough people to plant %d acres.\n", 10 * Pop);
else valid = TRUE;
} while(valid == FALSE);
return plant;
}
int
inputsell(void)
{
int landbal, sell, price;
Boolean valid = FALSE;
landbal = Land - plantland;
do
{
print("The price of land is %d bushels per acre.\n", landprice);
print("How many acres do you wish to sell? ");
sell = inputiexpr();
if(sell < 0)
print("Sire, it is impossible to sell a negative amount of land.\nIf you wish to buy land, enter \"0\", then select (B)uy from the menu.\n");
else if(sell > landbal) print("Sire, you have only %d acres of land.\n", landbal);
else valid = TRUE;
} while(valid == FALSE);
return sell;
}
void
microcalc(void)
{
}
Plancmd
planmenu(void)
{
int cmdch = 0;
Plancmd cmd;
2020-07-01 14:56:44 +00:00
print("\n Plan for Year %d\n", Year);
print(" Grain in storage %7d | Land (price %2d) %7d\n", Grain, landprice, Land);
if(landsale > 0)
print(" From land sale %7d | Sell %7d\n", landsale * landprice, landsale);
if(landsale < 0)
print(" For land purchase%7d | Purchase %7d\n", landsale * landprice * -1, landsale * -1);
if(landsale != 0)
print("%s Net %7d | Net %7d\n", MSGPLTOT, Grain + (landsale * landprice), Land - landsale);
if(foodgrain > 0) print(" Provide for food%7d |\n", foodgrain);
if(plantland > 0)
print(" Provide for seed%7d | Plant %7d\n", plantland / 2, plantland);
if(foodgrain > 0 || plantland > 0)
print("%s Balance %7d | Fallow acres %7d\n", MSGPLTOT,
Grain + (landsale * landprice) - foodgrain - (plantland / 2),
Land - landsale - plantland);
print("\n Population: %6d (need food: %6d; can farm: %6d)\n", Pop, Pop * 20, Pop * 10);
2020-06-19 16:19:33 +00:00
print(MSGMENU);
if(write(CONSCTL, "rawon", 5) != 5) exits("\ncan't turn off echo\n");
while(cmdch == 0)
{
print("What is your choice? [fpbserq] ");
cmdch = getchar();
if(cmdch == EOF) exits("\nerror reading terminal input\n");
print("%c\n", cmdch);
switch(cmdch)
{
case 'b':
cmd = BUY;
break;
case 'e':
cmd = EXEC;
break;
case 'f':
cmd = FEED;
break;
case 'p':
cmd = PLANT;
break;
case 'q':
cmd = QUIT;
break;
case 'r':
cmd = RESET;
break;
case 's':
cmd = SELL;
break;
default:
cmdch = 0;
}
}
if(write(CONSCTL, "rawoff", 6) != 6) exits("\ncan't turn on echo\n");
return cmd;
}
2020-06-14 06:04:08 +00:00
Boolean
planyear(void)
{
2020-06-15 14:24:12 +00:00
Boolean play = TRUE;
Plancmd cmd;
2020-06-19 16:19:33 +00:00
foodgrain = landsale = plantland = 0;
2020-06-15 14:24:12 +00:00
do
{
cmd = planmenu();
switch(cmd)
{
case BUY:
2020-06-19 16:19:33 +00:00
landsale = -1 * inputbuy();
2020-06-15 14:24:12 +00:00
break;
case SELL:
2020-06-19 16:19:33 +00:00
landsale = inputsell();
2020-06-15 14:24:12 +00:00
break;
case FEED:
2020-06-19 16:19:33 +00:00
foodgrain = inputfood();
2020-06-15 14:24:12 +00:00
break;
case PLANT:
2020-06-19 16:19:33 +00:00
plantland = inputplant();
2020-06-15 14:24:12 +00:00
break;
case RESET:
2020-06-19 16:19:33 +00:00
foodgrain = landsale = plantland = 0;
2020-06-15 14:24:12 +00:00
break;
case EXEC:
2020-06-21 14:52:06 +00:00
print("So it shall be written, so it shall be done.\n");
2020-06-15 14:24:12 +00:00
break;
case CALC:
2020-06-21 14:52:06 +00:00
// microcalc();
2020-06-15 14:24:12 +00:00
break;
case QUIT:
play = FALSE;
break;
}
} while(cmd != EXEC && cmd != QUIT);
return play;
}
void
printdeposed(void)
{
print("Due to this extreme mismanagement you have not only been deposed and executed, but you have also been declared National Fink!\n");
2020-06-14 06:04:08 +00:00
}
void
printgreeting(void)
{
print(MSGDIV);
print("O %s One, I beg to report,\n", HONORIFIC[nrand(MAXHONOR + 1)]);
}
void
printlandprice(void)
{
print("The price of land is %d bushels of grain per acre.\n", landprice);
}
void
printstatus(void)
{
2020-06-19 16:19:33 +00:00
print("The population is %d.\n", Pop);
2020-06-14 06:04:08 +00:00
print("You own %d acres.\n", Land);
print("You have %d bushels of grain.\n", Grain);
}
void
printyearrept(void)
{
2020-06-15 14:24:12 +00:00
print("In Year %d,\n", Year);
2020-06-14 06:04:08 +00:00
print("%d bushels were harvested per acre, for a total harvest of %d bushels.\n",
harvyield, plantland * harvyield);
if(ratgrain > 0) print("Rats ate %d bushels of grain.\n", ratgrain);
if(newpop > 0) print("The population increased by %d.\n", newpop);
2020-06-21 14:52:06 +00:00
if(starvedeath > 0) print("%d people starved!\n", starvedeath);
if(plaguedeath > 0) print("A horrible plague struck! %d people have died!\n", plaguedeath);
2020-06-14 06:04:08 +00:00
}
Boolean
scanyes(char *prompt)
{
int in = 0;
2020-06-19 16:19:33 +00:00
if(write(CONSCTL, "rawon", 5) != 5) exits("\ncan't turn off echo\n");
2020-06-14 06:04:08 +00:00
while(in != 'y' && in != 'n')
{
2020-07-01 14:56:44 +00:00
print("%s [yn] ", prompt);
2020-06-14 06:04:08 +00:00
in = getchar();
if(in == EOF) exits("\nerror reading terminal input\n");
print("%c\n", in);
}
2020-06-19 16:19:33 +00:00
if(write(CONSCTL, "rawoff", 6) != 6) exits("\ncan't turn on echo\n");
2020-06-14 06:04:08 +00:00
print("\n");
return (in == 'y' ? TRUE : FALSE);
}
void
setlandprice(void)
{
landprice = nrand(10) + 17;
}
2020-07-01 14:56:44 +00:00