budget: Fix treasury tracking

The update simply updates each nation's nat_money as it goes.  Works.
Except it doesn't update when it runs on behalf of budget.  But it
still checks nat_money to determine whether the nation is solvent.
These checks are all broken.  Leads to massive mispredictions when
you'd go broke or solvent during a real update.

Track money unconditionally in nat_budget[].money.  Delay update of
nat_money until prod_nat().  Replace separate money[] by new
nat_budget[].start_money.  Closes bug#235.

Remaining difference between budget and update in the update test:

* #1: budget mispredicts plane #100 gets built (to be fixed)

* #2: budget shows ship, plane and land unit maintenance when broke,
      but update damages them instead (correct)

* #2: sector -14,0 converts, quadrupling its taxes (correct)

* #4 & #5: bank with dust and bars taken over by che (correct)

* #4: plague deaths (correct)

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
This commit is contained in:
Markus Armbruster 2016-06-19 11:32:36 +02:00
parent 058268595f
commit 10789a0365
14 changed files with 78 additions and 99 deletions

View file

@ -64,11 +64,13 @@ struct budget {
struct budg_item bm[BUDG_BLD_MAX + 1];
/* population, taxes, military payroll, bank interest */
struct budg_item civ, mil, uw, bars;
/* treasury */
int start_money; /* at beginning of update */
int money; /* current */
};
/* main.c */
extern struct budget nat_budget[MAXNOC];
extern int money[MAXNOC];
extern int pops[MAXNOC];
extern int tpops[MAXNOC];
/* nat.c */

View file

@ -151,9 +151,10 @@ calc_all(void)
int etu = etu_per_update;
memset(nat_budget, 0, sizeof(nat_budget));
np = getnatp(player->cnum);
budget->start_money = budget->money = np->nat_money;
bp = bp_alloc();
for (n = 0; NULL != (sp = getsectid(n)); n++) {
bp_set_from_sect(bp, sp);
if (sp->sct_own == player->cnum) {
@ -180,6 +181,9 @@ calc_all(void)
prod_plane(etu, player->cnum, bp, 1);
prod_land(etu, player->cnum, bp, 1);
if (CANT_HAPPEN(np->nat_money != budget->start_money))
np->nat_money = budget->start_money;
free(bp);
return budget;
}

View file

@ -30,7 +30,7 @@
* Dave Pare, 1986
* Thomas Ruschak, 1993
* Steve McClure, 1998
* Markus Armbruster, 2004-2011
* Markus Armbruster, 2004-2016
*/
#include <config.h>
@ -52,7 +52,6 @@ finish_sects(int etu)
{
static double *import_cost;
struct sctstr *sp;
struct natstr *np;
int n;
struct rusage rus1, rus2;
@ -72,8 +71,7 @@ finish_sects(int etu)
continue;
if (sp->sct_own == 0)
continue;
np = getnatp(sp->sct_own);
if (np->nat_money < 0)
if (nat_budget[sp->sct_own].money < 0)
continue;
dodeliver(sp);
}
@ -93,8 +91,7 @@ finish_sects(int etu)
for (n = 0; NULL != (sp = getsectid(n)); n++) {
if (!sp->sct_own)
continue;
np = getnatp(sp->sct_own);
if (np->nat_money < 0)
if (nat_budget[sp->sct_own].money < 0)
continue;
dodistribute(sp, EXPORT, import_cost[n]);
}
@ -105,8 +102,7 @@ finish_sects(int etu)
sp->sct_off = 0;
if (!sp->sct_own)
continue;
np = getnatp(sp->sct_own);
if (np->nat_money < 0)
if (nat_budget[sp->sct_own].money < 0)
continue;
dodistribute(sp, IMPORT, import_cost[n]);
}

View file

@ -48,7 +48,7 @@
#include "prototypes.h"
#include "update.h"
static void upd_land(struct lndstr *, int, struct natstr *, struct bp *, int);
static void upd_land(struct lndstr *, int, struct bp *, int);
static void landrepair(struct lndstr *, struct natstr *, struct bp *,
int, struct budget *);
static int feed_land(struct lndstr *, int);
@ -59,9 +59,7 @@ prod_land(int etus, int natnum, struct bp *bp, int build)
{
struct lndstr *lp;
struct sctstr *sp;
struct natstr *np;
int i;
int start_money;
for (i = 0; (lp = getlandp(i)); i++) {
if (lp->lnd_own == 0)
@ -78,21 +76,17 @@ prod_land(int etus, int natnum, struct bp *bp, int build)
sp = getsectp(lp->lnd_x, lp->lnd_y);
if (sp->sct_type == SCT_SANCT)
continue;
np = getnatp(lp->lnd_own);
start_money = np->nat_money;
upd_land(lp, etus, np, bp, build);
if (player->simulation)
np->nat_money = start_money;
upd_land(lp, etus, bp, build);
}
}
static void
upd_land(struct lndstr *lp, int etus,
struct natstr *np, struct bp *bp, int build)
upd_land(struct lndstr *lp, int etus, struct bp *bp, int build)
/* build = 1, maintain = 0 */
{
struct budget *budget = &nat_budget[lp->lnd_own];
struct lchrstr *lcp;
struct lchrstr *lcp = &lchr[lp->lnd_type];
struct natstr *np = getnatp(lp->lnd_own);
int pstage, ptime;
int min = morale_base - (int)np->nat_level[NAT_HLEV];
int n, mult, cost, eff_lost;
@ -101,9 +95,8 @@ upd_land(struct lndstr *lp, int etus,
if (lp->lnd_retreat < min)
lp->lnd_retreat = min;
lcp = &lchr[(int)lp->lnd_type];
if (build == 1) {
if (!lp->lnd_off && np->nat_money >= 0)
if (!lp->lnd_off && budget->money >= 0)
landrepair(lp, np, bp, etus, budget);
if (!player->simulation)
lp->lnd_off = 0;
@ -115,7 +108,7 @@ upd_land(struct lndstr *lp, int etus,
mult *= 3;
budget->bm[BUDG_LND_MAINT].count++;
cost = -(mult * etus * MIN(0.0, money_land * lcp->l_cost));
if (np->nat_money < cost && !player->simulation) {
if (budget->money < cost && !player->simulation) {
eff_lost = etus / 5;
if (lp->lnd_effic - eff_lost < LAND_MINEFF)
eff_lost = lp->lnd_effic - LAND_MINEFF;
@ -126,7 +119,7 @@ upd_land(struct lndstr *lp, int etus,
}
} else {
budget->bm[BUDG_LND_MAINT].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
}
if (!player->simulation) {
@ -249,7 +242,7 @@ landrepair(struct lndstr *land, struct natstr *np, struct bp *bp, int etus,
cost = roundavg(mult * lp->l_cost * build / 100.0);
budget->bm[BUDG_LND_BUILD].count += !!build;
budget->bm[BUDG_LND_BUILD].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
if (!player->simulation)
land->lnd_effic += (signed char)build;
}

View file

@ -47,7 +47,6 @@
#include "update.h"
struct budget nat_budget[MAXNOC];
int money[MAXNOC];
int pops[MAXNOC];
int tpops[MAXNOC];
@ -87,10 +86,9 @@ update_main(void)
memset(nat_budget, 0, sizeof(nat_budget));
memset(pops, 0, sizeof(pops));
for (n = 0; n < MAXNOC; n++) {
money[n] = 0;
if (!(np = getnatp(n)))
continue;
money[n] = np->nat_money;
nat_budget[n].start_money = nat_budget[n].money = np->nat_money;
tpops[n] = count_pop(n);
}

View file

@ -206,6 +206,7 @@ prod_nat(int etu)
np->nat_level[NAT_RLEV] += rlev;
if (tlev != 0.0)
np->nat_level[NAT_TLEV] += tlev;
bm = nat_budget[n].bm;
sea_money = bm[BUDG_SHP_MAINT].money + bm[BUDG_SHP_BUILD].money;
air_money = bm[BUDG_PLN_MAINT].money + bm[BUDG_PLN_BUILD].money;
@ -214,8 +215,12 @@ prod_nat(int etu)
wu(0, n,
"Army delta $%d, Navy delta $%d, Air force delta $%d\n",
lnd_money, sea_money, air_money);
if (CANT_HAPPEN(np->nat_money != nat_budget[n].start_money))
nat_budget[n].money += np->nat_money - nat_budget[n].start_money;
wu(0, n, "money delta was $%d for this update\n",
np->nat_money - money[n]);
nat_budget[n].money - nat_budget[n].start_money);
np->nat_money = nat_budget[n].money;
if (opt_LOSE_CONTACT) {
for (cn = 1; cn < MAXNOC; cn++) {
if ((cnp = getnatp(cn)) != NULL)

View file

@ -45,7 +45,7 @@
#include "ship.h"
#include "update.h"
static void upd_plane(struct plnstr *, int, struct natstr *, struct bp *, int);
static void upd_plane(struct plnstr *, int, struct bp *, int);
static void planerepair(struct plnstr *, struct natstr *, struct bp *,
int, struct budget *);
@ -54,9 +54,7 @@ prod_plane(int etus, int natnum, struct bp *bp, int buildem)
/* Build = 1, maintain =0 */
{
struct plnstr *pp;
struct natstr *np;
int i;
int start_money;
for (i = 0; (pp = getplanep(i)); i++) {
if (pp->pln_own == 0)
@ -77,24 +75,20 @@ prod_plane(int etus, int natnum, struct bp *bp, int buildem)
continue;
}
np = getnatp(pp->pln_own);
start_money = np->nat_money;
upd_plane(pp, etus, np, bp, buildem);
if (player->simulation)
np->nat_money = start_money;
upd_plane(pp, etus, bp, buildem);
}
}
static void
upd_plane(struct plnstr *pp, int etus,
struct natstr *np, struct bp *bp, int build)
upd_plane(struct plnstr *pp, int etus, struct bp *bp, int build)
{
struct budget *budget = &nat_budget[pp->pln_own];
struct plchrstr *pcp = &plchr[(int)pp->pln_type];
struct plchrstr *pcp = &plchr[pp->pln_type];
struct natstr *np = getnatp(pp->pln_own);
int mult, cost, eff_lost;
if (build == 1) {
if (!pp->pln_off && np->nat_money >= 0)
if (!pp->pln_off && budget->money >= 0)
planerepair(pp, np, bp, etus, budget);
if (!player->simulation)
pp->pln_off = 0;
@ -104,7 +98,7 @@ upd_plane(struct plnstr *pp, int etus,
mult = 2;
budget->bm[BUDG_PLN_MAINT].count++;
cost = -(mult * etus * MIN(0.0, pcp->pl_cost * money_plane));
if (np->nat_money < cost && !player->simulation) {
if (budget->money < cost && !player->simulation) {
eff_lost = etus / 5;
if (pp->pln_effic - eff_lost < PLANE_MINEFF)
eff_lost = pp->pln_effic - PLANE_MINEFF;
@ -115,12 +109,12 @@ upd_plane(struct plnstr *pp, int etus,
}
} else {
budget->bm[BUDG_PLN_MAINT].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
}
/* flight pay is 5x the pay received by other military */
cost = etus * pcp->pl_mat[I_MILIT] * -money_mil * 5;
budget->bm[BUDG_PLN_MAINT].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
}
}
@ -205,8 +199,7 @@ planerepair(struct plnstr *pp, struct natstr *np, struct bp *bp, int etus,
cost = roundavg(mult * build * pcp->pl_cost / 100.0);
budget->bm[BUDG_PLN_BUILD].count += !!build;
budget->bm[BUDG_PLN_BUILD].money -= cost;
np->nat_money -= cost;
if (!player->simulation) {
budget->money -= cost;
if (!player->simulation)
pp->pln_effic += (signed char)build;
}
}

View file

@ -101,10 +101,6 @@ prepare_sects(int etu)
for (n = 0; NULL != (np = getnatp(n)); n++) {
upd_slmilcosts(etu, np->nat_cnum);
pay_reserve(np, etu);
np->nat_money += nat_budget[n].mil.money;
np->nat_money += nat_budget[n].civ.money;
np->nat_money += nat_budget[n].uw.money;
np->nat_money += nat_budget[n].bars.money;
}
}
@ -123,15 +119,18 @@ tax(struct sctstr *sp, int etu, int *pop)
civ_tax /= 4;
budget->civ.count += sp->sct_item[I_CIVIL];
budget->civ.money += civ_tax;
budget->money += civ_tax;
uw_tax = (int)(0.5 + sp->sct_item[I_UW] * sp->sct_effic *
etu * money_uw / 100);
budget->uw.count += sp->sct_item[I_UW];
budget->uw.money += uw_tax;
budget->money += uw_tax;
mil_pay = sp->sct_item[I_MILIT] * etu * money_mil;
budget->mil.count += sp->sct_item[I_MILIT];
budget->mil.money += mil_pay;
budget->money += mil_pay;
/*
* only non-captured civs add to census for nation
@ -145,7 +144,7 @@ upd_slmilcosts(int etu, natid n)
{
struct shpstr *sp;
struct lndstr *lp;
int mil, i;
int mil, i, mil_pay;
mil = 0;
@ -161,8 +160,10 @@ upd_slmilcosts(int etu, natid n)
mil += lp->lnd_item[I_MILIT];
}
mil_pay = mil * etu * money_mil;
nat_budget[n].mil.count += mil;
nat_budget[n].mil.money += mil * etu * money_mil;
nat_budget[n].mil.money += mil_pay;
nat_budget[n].money += mil_pay;
}
void
@ -173,11 +174,14 @@ bank_income(struct sctstr *sp, int etu)
inc = (int)(sp->sct_item[I_BAR] * etu * bankint * sp->sct_effic / 100);
nat_budget[sp->sct_own].bars.count += sp->sct_item[I_BAR];
nat_budget[sp->sct_own].bars.money += inc;
nat_budget[sp->sct_own].money += inc;
}
void
pay_reserve(struct natstr *np, int etu)
{
nat_budget[np->nat_cnum].mil.money
+= (int)(np->nat_reserve * money_res * etu);
int pay = (int)(np->nat_reserve * money_res * etu);
nat_budget[np->nat_cnum].mil.money += pay;
nat_budget[np->nat_cnum].money += pay;
}

View file

@ -163,8 +163,7 @@ produce(struct natstr *np, struct sctstr *sp)
budget->prod[sp->sct_type].count += actual;
budget->prod[sp->sct_type].money -= cost;
if (!player->simulation)
np->nat_money -= cost;
budget->money -= cost;
if (CANT_HAPPEN(p_e <= 0.0))
return;

View file

@ -114,8 +114,7 @@ enlist(struct natstr *np, short *vec, int etu)
nat_budget[np->nat_cnum].prod[SCT_ENLIST].count += enlisted;
nat_budget[np->nat_cnum].prod[SCT_ENLIST].money -= enlisted * 3;
if (!player->simulation)
np->nat_money -= enlisted * 3;
nat_budget[np->nat_cnum].money -= enlisted * 3;
}
/* Fallout is calculated here. */
@ -258,7 +257,7 @@ produce_sect(struct natstr *np, int etu, struct bp *bp)
do_feed(sp, np, etu, 0);
if (sp->sct_off || np->nat_money < 0) {
if (sp->sct_off || budget->money < 0) {
sp->sct_avail = 0;
bp_set_from_sect(bp, sp);
continue;
@ -268,17 +267,15 @@ produce_sect(struct natstr *np, int etu, struct bp *bp)
cost = etu * dchr[sp->sct_type].d_maint;
budget->bm[BUDG_SCT_MAINT].count++;
budget->bm[BUDG_SCT_MAINT].money -= cost;
if (!player->simulation)
np->nat_money -= cost;
budget->money -= cost;
}
if ((sp->sct_effic < 100 || sp->sct_type != sp->sct_newtype) &&
np->nat_money >= 0) {
budget->money >= 0) {
cost = roundavg(buildeff(sp));
budget->bm[BUDG_SCT_BUILD].count++;
budget->bm[BUDG_SCT_BUILD].money -= cost;
if (!player->simulation)
np->nat_money -= cost;
budget->money -= cost;
}
if (sp->sct_type == SCT_ENLIST && sp->sct_effic >= 60 &&
@ -291,7 +288,7 @@ produce_sect(struct natstr *np, int etu, struct bp *bp)
*/
if (sp->sct_effic >= 60) {
if (np->nat_money >= 0 && dchr[sp->sct_type].d_prd >= 0)
if (budget->money >= 0 && dchr[sp->sct_type].d_prd >= 0)
produce(np, sp);
}

View file

@ -50,7 +50,7 @@
#include "ship.h"
#include "update.h"
static void upd_ship(struct shpstr *, int, struct natstr *, struct bp *, int);
static void upd_ship(struct shpstr *, int, struct bp *, int);
static void shiprepair(struct shpstr *, struct natstr *, struct bp *,
int, struct budget *);
static int feed_ship(struct shpstr *, int);
@ -60,9 +60,7 @@ prod_ship(int etus, int natnum, struct bp *bp, int build)
/* build = 1, maintain = 0 */
{
struct shpstr *sp;
struct natstr *np;
int i;
int start_money;
for (i = 0; (sp = getshipp(i)); i++) {
if (sp->shp_own == 0)
@ -76,22 +74,18 @@ prod_ship(int etus, int natnum, struct bp *bp, int build)
continue;
}
np = getnatp(sp->shp_own);
start_money = np->nat_money;
upd_ship(sp, etus, np, bp, build);
if (player->simulation)
np->nat_money = start_money;
upd_ship(sp, etus, bp, build);
}
}
static void
upd_ship(struct shpstr *sp, int etus,
struct natstr *np, struct bp *bp, int build)
upd_ship(struct shpstr *sp, int etus, struct bp *bp, int build)
/* build = 1, maintain = 0 */
{
struct budget *budget = &nat_budget[sp->shp_own];
struct mchrstr *mp = &mchr[sp->shp_type];
struct natstr *np = getnatp(sp->shp_own);
struct sctstr *sectp;
struct mchrstr *mp;
int pstage, ptime;
int oil_gained;
int max_oil;
@ -101,9 +95,8 @@ upd_ship(struct shpstr *sp, int etus,
int dep;
int n, mult, cost, eff_lost;
mp = &mchr[(int)sp->shp_type];
if (build == 1) {
if (!sp->shp_off && np->nat_money >= 0)
if (!sp->shp_off && budget->money >= 0)
shiprepair(sp, np, bp, etus, budget);
if (!player->simulation)
sp->shp_off = 0;
@ -113,7 +106,7 @@ upd_ship(struct shpstr *sp, int etus,
mult = 2;
budget->bm[BUDG_SHP_MAINT].count++;
cost = -(mult * etus * MIN(0.0, money_ship * mp->m_cost));
if (np->nat_money < cost && !player->simulation) {
if (budget->money < cost && !player->simulation) {
eff_lost = etus / 5;
if (sp->shp_effic - eff_lost < SHIP_MINEFF)
eff_lost = sp->shp_effic - SHIP_MINEFF;
@ -124,14 +117,14 @@ upd_ship(struct shpstr *sp, int etus,
}
} else {
budget->bm[BUDG_SHP_MAINT].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
}
if (!player->simulation) {
sectp = getsectp(sp->shp_x, sp->shp_y);
/* produce oil */
if (np->nat_money >= 0
if (budget->money >= 0
&& (mp->m_flags & M_OIL) && sectp->sct_type == SCT_WATER) {
product = &pchr[dchr[SCT_OIL].d_prd];
oil_gained = roundavg(total_work(100, etus,
@ -157,7 +150,7 @@ upd_ship(struct shpstr *sp, int etus,
sp->shp_item[I_OIL] += oil_gained;
}
/* produce fish */
if (np->nat_money >= 0
if (budget->money >= 0
&& (mp->m_flags & M_FOOD) && sectp->sct_type == SCT_WATER) {
sp->shp_item[I_FOOD]
+= roundavg(total_work(100, etus,
@ -317,7 +310,7 @@ shiprepair(struct shpstr *ship, struct natstr *np, struct bp *bp, int etus,
cost = roundavg(mult * mp->m_cost * build / 100.0);
budget->bm[BUDG_SHP_BUILD].count += !!build;
budget->bm[BUDG_SHP_BUILD].money -= cost;
np->nat_money -= cost;
budget->money -= cost;
if (!player->simulation)
ship->shp_effic += (signed char)build;
}

View file

@ -1,6 +1,5 @@
budget
| BUG: military payroll slightly low
| BUG: expenses ignore going broke
| TODO is it accurate?
production *
| Note: ignores going broke

View file

@ -1,5 +1,4 @@
budget
| BUG: expenses ignore becoming solvent
| TODO is it accurate?
production *
| TODO is it accurate?

View file

@ -225,23 +225,17 @@
Play#2 input budget
Play#2 command budget
Play#2 output Play#2 1 Sector Type Production Cost
Play#2 output Play#2 1 technical center 1 tech 300
Play#2 output Play#2 1 Ship building 2 ships 450
Play#2 output Play#2 1 Ship maintenance 3 ships 90
Play#2 output Play#2 1 Plane building 1 plane 360
Play#2 output Play#2 1 Plane maintenance 3 planes 144
Play#2 output Play#2 1 Unit building 1 unit 450
Play#2 output Play#2 1 Unit maintenance 3 units 90
Play#2 output Play#2 1 Sector building 26 sectors 209
Play#2 output Play#2 1 Sector maintenance 5 sectors 300
Play#2 output Play#2 1 Military payroll 1900 mil, 1000 res 9994
Play#2 output Play#2 1 Total expenses.....................................................12387
Play#2 output Play#2 1 Total expenses.....................................................10318
Play#2 output Play#2 1 Income from taxes 6800 civs, 4000 uws +1076
Play#2 output Play#2 1 Income from bars 400 bars +2865
Play#2 output Play#2 1 Total income.......................................................+3941
Play#2 output Play#2 1 Balance forward 100
Play#2 output Play#2 1 Estimated delta -8446
Play#2 output Play#2 1 Estimated new treasury.............................................-8346
Play#2 output Play#2 1 Estimated delta -6377
Play#2 output Play#2 1 Estimated new treasury.............................................-6277
Play#2 output Play#2 1 After processsing sectors, you will be broke!
Play#2 output Play#2 1 Sectors will not produce, distribute, or deliver!
Play#2 output Play#2 1
@ -279,13 +273,16 @@
Play#3 input budget
Play#3 command budget
Play#3 output Play#3 1 Sector Type Production Cost
Play#3 output Play#3 1 uranium mine 67 rad 156
Play#3 output Play#3 1 Sector building 56 sectors 39
Play#3 output Play#3 1 Sector maintenance 1 sector 60
Play#3 output Play#3 1 Military payroll 170 mil, 0 res 848
Play#3 output Play#3 1 Total expenses.......................................................848
Play#3 output Play#3 1 Total expenses......................................................1103
Play#3 output Play#3 1 Income from taxes 16549 civs, 150 uws +5540
Play#3 output Play#3 1 Total income.......................................................+5540
Play#3 output Play#3 1 Balance forward -100
Play#3 output Play#3 1 Estimated delta +4692
Play#3 output Play#3 1 Estimated new treasury..............................................4592
Play#3 output Play#3 1 Estimated delta +4437
Play#3 output Play#3 1 Estimated new treasury..............................................4337
Play#3 output Play#3 6 0 99
Play#3 input production *
Play#3 command production