/* Idle Theory - Economic Model 1 (Java 1.0.2) Derived from MOD11.BAS QuickBasic original. Chris Davis - 1998 System.out.println()s removed Mar 2003 */ import java.awt.*; import java.lang.*; import java.applet.*; import java.util.*; import java.net.*; public class Econ1 extends Applet implements Runnable { double elapsed_time = 0; double dt = 1.0; // actual time step double dt_requ = 8.0; // required time step double display_dt = 256.0; // display time step Economy ec; int frameNumber = -1; Thread animatorThread = null; boolean frozen = true; Checkbox checkbox1; GraphCanvas canvas2; TextArea t1; Button b1; Choice c1; int currentChoice = 0; // selected Choice index no. boolean actionChoice = false; // true to do something about it Label counter; Label day, avAge, variance; public int ctlTime[] = new int[15]; public int ctlAction[] = new int[15]; public double tvalue[] = new double[20]; public double tcost[] = new double[20]; public double tlife[] = new double[20]; public double justMarkup; public int nmen, ui; public int trading; public int avperiod; public String getAppletInfo() { return "Idle Theory - economic model 1\r\n"; } public void init() { Dimension d = size(); Double dbl; int i; // set up the GUI GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); setLayout(gridBag); b1 = new Button(); if ( frozen ) { b1.setLabel("Start"); } else { b1.setLabel("Stop"); } c.gridwidth = GridBagConstraints.EAST; c.gridx = 0; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; gridBag.setConstraints(b1, c); add(b1); counter = new Label("Day "); c.fill = GridBagConstraints.BOTH; c.gridx = 3; c.gridy = 0; c.gridwidth = 1; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; gridBag.setConstraints(counter, c); add(counter); c1 = new Choice(); c1.addItem("Trading on/off"); c1.addItem("Set Prices = zero"); c1.addItem("Set random prices"); c1.addItem("Set prices = Values"); c1.addItem("Double prices"); c1.addItem("Halve prices"); c1.addItem("Set Prices = Costs"); c1.addItem("Set Just Prices"); c1.addItem("Set Unjust 1-Price"); c1.addItem("Set Unjust 4-Price"); c1.addItem("Untooled idleness+"); c1.addItem("Untooled idleness-"); c1.addItem("Untooled idleness--"); c1.addItem("Limited credit"); c1.addItem("Unlimited credit"); c.fill = GridBagConstraints.BOTH; c.gridx = 1; c.gridy = 0; c.gridwidth = 2; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; gridBag.setConstraints(c1, c); add(c1); t1 = new TextArea("Text area", 10, 25); c.fill = GridBagConstraints.BOTH; c.gridx = 0; c.gridy = 1; c.gridwidth = 5; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; t1.resize( d.width, d.width-20 ); gridBag.setConstraints(t1, c); add(t1); t1.setEditable(true); canvas2 = new GraphCanvas(); c.fill = GridBagConstraints.BOTH; c.gridx = 0; c.gridy = 2; c.gridwidth = 5; c.gridheight= 1; c.weightx = 1.0; c.weighty = 1.0; canvas2.resize( d.width, 115 ); gridBag.setConstraints(canvas2, c); add(canvas2); validate(); /* Parameters allow Choice items to be programmed events. Thus CT0 = 110 and CA0 = 4 means "double prices on day 110" And CT1 = 230 and CA1 = 7 means "set just prices on day 230" */ // get timer control parameters. // These determine when some action takes place. for ( int pn = 0; pn <15; pn++ ) { try { ctlTime[pn] = Integer.parseInt( getParameter("CT" + Integer.toString(pn) ) ); } catch( Exception e ) { ctlTime[pn] = 0; } } // get control action parameters. // These determine what action takes place at ctlTime[pn]. for ( int pn = 0; pn <15; pn++ ) { try { ctlAction[pn] = Integer.parseInt( getParameter("CA" + Integer.toString(pn) ) ); } catch( Exception e ) { ctlAction[pn] = 0; } } /* get number of men in economy */ try { nmen = Integer.parseInt( getParameter("NMEN" ) ); } catch( Exception e ) { nmen = 4; } /* get untooled idleness */ try { ui = Integer.parseInt( getParameter("UI" ) ); } catch( Exception e ) { ui = 0; } /* get trade/no trade */ try { trading = Integer.parseInt( getParameter("TRADE" ) ); } catch( Exception e ) { trading = 1; } /* get averaging period */ try { avperiod = Integer.parseInt( getParameter("AVPERIOD" ) ); } catch( Exception e ) { avperiod = 50; } /* get tool parameters - tool value, cost, and lifetime - */ for ( int pn = 0; pn <20; pn++ ) { try { dbl = Double.valueOf( getParameter("TV" + Integer.toString(pn) ) ); tvalue[pn] = dbl.doubleValue(); } catch( Exception e ) { tvalue[pn] = 0.002; } } /* set Just price of tool 1 as justmarkup * tool 1 cost */ try { dbl = Double.valueOf( getParameter("JUST") ); justMarkup = dbl.doubleValue(); } catch( Exception e ) { justMarkup = 1.1; } for ( int pn = 0; pn <20; pn++ ) { try { dbl = Double.valueOf( getParameter("TC" + Integer.toString(pn) ) ); tcost[pn] = dbl.doubleValue(); } catch( Exception e ) { tcost[pn] = 0.001; } } for ( int pn = 0; pn <20; pn++ ) { try { dbl = Double.valueOf( getParameter("TL" + Integer.toString(pn) ) ); tlife[pn] = dbl.doubleValue(); } catch( Exception e ) { tlife[pn] = 10.0; } } //initialize economy ec = new Economy( this ); ec.init(); } public void start() { if (animatorThread == null) { animatorThread = new Thread(this); animatorThread.start(); } } public void stop() { if ( animatorThread != null ) { animatorThread.stop(); animatorThread = null; } } public void destroy() { } public boolean action(Event e, Object arg) { // start/stop button if ( e.target == b1 ) { frozen = !frozen; if ( frozen ) { b1.setLabel("Start"); } else { b1.setLabel("Stop"); } } // choice list if ( e.target == c1 ) { currentChoice = c1.getSelectedIndex(); actionChoice = true; // if frozen ( perhaps only just init'd ) perform choice action // immediately, else do in main loop engine. if ( frozen == true ) { actionChoice = ChoiceOptions.doChoiceAction( currentChoice, ec ); } } return true; } public boolean mouseDown(Event e, int x, int y) { return true; } public void run() { Thread.currentThread().setPriority(Thread.MIN_PRIORITY); //This is the animation loop. while (Thread.currentThread() == animatorThread) { try { if ( !frozen ) { //Advance the animation frame. frameNumber++; // work out species idleness ec.engine(); // canvas1.redraw(); // Display graphs canvas2.repaint(); animatorThread.sleep(30); } else { animatorThread.sleep(500); } } catch ( InterruptedException e ) { stop(); } } } } /****** Tool **********************************************************/ /* These tools only save time in basic self-maintence work */ class Tool { public double value; // product value V public double dvalue; // product daily value DV public double lifetime; // product lifetime L public double cost; // product cost C public Tool( double v, double c, double l ) { this.value = v; this.cost = c; this.lifetime = l; this.dvalue = v / l; } } /****** Man - member of economy ***************************************/ class Man { Economy economy; public static int maxHealth = 3; public boolean alive; // life status public int health; // health (store of days of life) public int id; // identity number public double idleness; // idleness (fraction) public double untooledIdleness; // untooled idleness (fraction) public int avperiod; // averaging period public int avindex; // current index into average array public int avI[] = new int[50]; // average idleness array (%) public double purse; // IOUs in purse public double IOUissue; // IOUs issued public double IOUlimit; // IOU issue limit (credit limit) public boolean creditworthy; // true if can issue IOUs public boolean discriminates; // true if buys when price < value public int shoptool; // product name/number public double shopPrice; // product price P public double shopStock; // product stock for sale (number of) public double shopStockTarget; // product stock-for-sale target public double stock[] = new double[20]; // tools-in-use stock (number) public int numGoodsOwned; // number of different goods owned + used. public double predictedIncome; public double predictedIdleness; public double avPurse; // purse over average period (10 days?) public double avIdleness; // idleness .. .. .. .. (%) public boolean trading; // trading /not trading /* Man constructor */ public Man( int id, double price, double stock, Economy e ) { super(); economy = e; this.id = id; this.alive = true; this.health = maxHealth; this.creditworthy = true; this.untooledIdleness = (double)economy.ui / 100.0; this.discriminates = false; this.trading = economy.trading; this.shoptool = id; if ( this.trading ) { this.shopPrice = price; } else { this.shopPrice = 0.0; } this.shopStock = stock; this.shopStockTarget = 3.0; this.stock[0] = 1.0; this.numGoodsOwned = 0; for ( int n = 1; n <= economy.nmen; n++ ) { if ( this.trading ) { this.stock[n] = 1.0 / (double)id; this.numGoodsOwned += 1; } else { if ( n == this.id ) { this.stock[n] = 0.5; this.numGoodsOwned += 1; } else { this.stock[n] = 0.0; } } } this.avindex = 0; this.avperiod = economy.avperiod; } /* Perform basic self-maintenance work, using tools. Each tool saves some amount of work per day. */ public void doDailyBasicWork() { double toolDailyValue, toolDailyUsage; this.idleness = this.untooledIdleness; for ( int n = 1; n <= economy.ntools; n++ ) { toolDailyValue = economy.t[n].dvalue; // time saved per day toolDailyUsage = 1.0 / economy.t[n].lifetime; // amount used per day if ( this.stock[n] < toolDailyUsage ) { toolDailyValue = this.stock[n] * economy.t[n].value; toolDailyUsage = this.stock[n]; } this.idleness += toolDailyValue; this.stock[n] -= toolDailyUsage; if ( this.idleness > 1.0 ) this.idleness = 1.0; // tools can't increase idleness above 1. /* the following code isn't correct when tools and luxuries are mixed because the presence of luxuries will prevent death */ if ( toolDailyValue < 0 ) { if ( this.idleness < 0.0 ) this.idleness = 0.0; // luxuries can't decrease idleness below 0. } } } /* Produce tools for sale in shop */ public void makeTool() { double toolCost, requWork, actualWork; toolCost = economy.t[this.shoptool].cost; if ( toolCost > 0 ) { if ( this.shopStock < this.shopStockTarget ) { if ( this.idleness > 0.0 ) { requWork = ( this.shopStockTarget - this.shopStock ) * toolCost; actualWork = Math.min( requWork, this.idleness ); this.shopStock += actualWork / toolCost; this.idleness -= actualWork; } } } } /* buy in as much labour as possible with IOUs in purse */ public void buyLabour() { double least; for ( int n = 1; n <= economy.nmen; n++ ) { if ( this.purse > 0.0 ) { // if any money in purse.. if ( economy.m[n].id != this.id ) { // not cancelling own IOUs.. least = this.purse; if ( economy.m[n].IOUissue < least ) least = economy.m[n].IOUissue; if ( economy.m[n].idleness < least ) least = economy.m[n].idleness; if ( least < 0.0 ) least = 0.0; // catch negative values this.idleness += least; // increase idleness economy.m[n].idleness -= least; // decrease IOU issuer idleness this.purse -= least; // decrease purse economy.m[n].IOUissue -= least; // cancel IOUs issued } } } } /* if short of tools, buy some, if necessary issuing IOUs */ /* assumes than man N sells tool N */ public void buyTools() { double price, value , iou; int t1 = 1; int t2 = economy.ntools; if ( !this.trading ) { t1 = this.id; t2 = this.id; } for ( int n = t1; n <= t2; n++ ) { if ( economy.m[n].trading || (economy.m[n].id == this.id) ) { // if tool n producer is trading if ( this.stock[n] < (1.0 / economy.t[n].lifetime) ) { // if he's almost run out... price = economy.m[n].shopPrice; value = economy.t[n].value; if ( (price < value) || !this.discriminates ) { // if price is worth paying... if ( economy.m[n].shopStock >= 1.0 ) { // if at least one in the shop.. if ( (price > this.purse) && this.creditworthy ) { iou = price - this.purse; // issue IOUs.. this.IOUissue += iou; this.purse += iou; // ..and transfer to purse. } if ( this.purse >= price ) { this.purse -= price; // pay for tool economy.m[n].purse += price; this.stock[n] += 1.0; // receive one tool economy.m[n].shopStock -= 1.0; } } else { } } } } } } /* count tools owned and used */ public void countNumGoodsOwned() { this.numGoodsOwned = 0; for ( int n = 1; n <= economy.nmen; n++ ) { if ( this.stock[n] > 0.0 ) this.numGoodsOwned += 1; } } /* set health. If idleness < 0, reduce health, otherwise increment to maxHealth. If health falls to zero, set alive = false */ public void setHealth() { if ( this.idleness < 0 ) { this.health -= 1; if ( this.health == 0 ) { this.setDead(); } } else { this.health += 1; if ( this.health > maxHealth ) this.health = maxHealth; } } /* set dead */ public void setDead() { this.alive = false; this.idleness = -1.0; for ( int n = 0; n < this.avperiod; n++ ) this.setavI(); } /* Set creditworthiness. If over IOU limit, remove creditworthiness. This stops men issuing unlimited IOUs. (zero IOUlimit = no credit limit) */ public void setCreditworthy() { if ( this.IOUlimit > 0.0 ) { if ( this.IOUissue > this.IOUlimit ) { this.creditworthy = false; } else { this.creditworthy = true; } } } /* set IOU credit limit */ public void setCreditlimit( double limit ) { if ( this.IOUlimit > 0.0 ) this.IOUlimit = limit; } /* enter current idleness into average-period idleness array */ public void setavI() { this.avI[avindex] = (int)(this.idleness * 100.0); avindex++; if ( avindex >= avperiod ) avindex = 0; } /* return average-period idleness */ public double getavI() { int isum = 0; for ( int i = 0; i < this.avperiod; i++ ) { isum += this.avI[ i ]; } return ( (double)isum / ((double)this.avperiod * 100.0 ) ); } /* paint Man */ public void paintMan( Graphics offGraphics, int gridsize, int kind, int xoffset, int repstep ) { } } // Economy class variables, constructors and methods // class Economy { Econ1 thisApplet; public static final int NMAX = 20; // max number of men public double meanIdleness; // predicted mean idleness public TextFormatter scrtext; public boolean trading; // trading/not-trading flag public double justMarkup; // just price markup public Man m[] = new Man[NMAX]; // array of men public int nmen = 4; // number of men public Tool t[] = new Tool[NMAX]; // array of tools public int ntools = 4; // number of tools public int day = 1; // day counter public int redLetterDay; // flag some event public double totprice; // total price of all tools public int index = 1; // re-indexer public int avperiod; // averaging period public int nextAvDay = avperiod; public int ctlPointer = 0; // choice event control public int ui; // untooled idleness // Constructor allows access to Applet public Economy( Econ1 a ) { super(); thisApplet = a; } public void init() { day = 1; redLetterDay = -1; /* define tool V, C, L */ for ( int n = 0; n < 20; n++ ) { t[n] = new Tool( thisApplet.tvalue[n], thisApplet.tcost[n], thisApplet.tlife[n] ); } /* set number of men in economy */ nmen = thisApplet.nmen; if ( nmen > NMAX ) nmen = NMAX; ntools = nmen; /* set untooled idleness */ ui = thisApplet.ui; /* set trading/ not trading */ trading = true; if ( thisApplet.trading == 0 ) trading = false; /* set just price markup to be used, if required */ justMarkup = thisApplet.justMarkup; /* set averaging period */ avperiod = thisApplet.avperiod; if ( avperiod > 50 ) avperiod = 50; if ( avperiod < 1 ) avperiod = 1; /* define each man's shop tool, price, and stock */ for ( int n = 1; n <= nmen; n++ ) { m[n] = new Man( n, 0.0, 3.0, this); } /* random pricing for ( int n = 1; n <= nmen; n++ ) { int i = m[n].shoptool; double v = (t[i].value - t[i].cost) * Math.random(); m[n].shopPrice = t[i].cost + v; } */ /* set unlimited IOU issue limit */ for ( int n = 1; n <= nmen; n++ ) { totprice += m[n].shopPrice; } for ( int n = 1; n <= nmen; n++ ) { m[n].IOUlimit = 0; } meanIdleness = predictMeanIdleness(); for ( int n = 1; n <= nmen; n++ ) { m[n].predictedIncome = predictIncome(n); } for ( int n = 1; n <= nmen; n++ ) { m[n].predictedIdleness = predictIdleness(n); for ( int i = 0; i < m[n].avperiod; i++ ) { m[n].avI[i] = (int)( 0.0 * 100.0 ); } } /* set up TextArea */ scrtext = new TextFormatter( this ); } /* The engine works out who does what during each 'day' of the economy. First everyone does their basic work, and then in the time remaining they may make tools. Then they buy labour with IOUs they hold, and use the bought labour to increase their idle time that day. Finally, they buy tools, issuing IOUs if necessary. If it is objected that they can't buy labour later in the day for work they have already done, the response is that rather than do the work, they first calculate how much work they will do that day, and then buy labour to actually do it, and then the calculated work is done. If it is asked why men can't issue IOUs to buy labour, the response is that it is a redundant exercise, because an IOU is simply a promise to perform some amount of labour. */ public void engine() { int i, n; thisApplet.counter.setText( "Day " + Integer.toString( day ) ); while ( thisApplet.ctlTime[ctlPointer] == day ) { thisApplet.actionChoice = ChoiceOptions.doChoiceAction( thisApplet.ctlAction[ctlPointer], this ); ctlPointer++; } /* action any selected Choices */ if ( thisApplet.actionChoice ) thisApplet.actionChoice = ChoiceOptions.doChoiceAction( thisApplet.currentChoice, this ); /* have each man perform daily basic work */ for ( n=1; n <= nmen; n++ ) { if ( m[n].alive ) m[n].doDailyBasicWork(); } /* have each man buy labour with IOUs he holds SWAPPED */ for ( n=index; n <= index+nmen; n++ ) { if ( m[reindex(n)].alive ) m[ reindex(n)].buyLabour(); } /* have each man make a tool, if shop stocks are low SWAPPED */ for ( n=1; n <= nmen; n++ ) { if ( m[n].alive ) m[n].makeTool(); } /* have each man buy tools, if necessary issuing IOUs */ for ( n=index; n <= index+nmen; n++ ) { if ( m[reindex(n)].alive ) m[ reindex(n)].buyTools(); } /* set health status and creditworthiness*/ for ( n=1; n <= nmen; n++ ) { if ( m[n].alive ) m[n].setHealth(); if ( m[n].alive ) m[n].setCreditworthy(); if ( m[n].alive ) m[n].setCreditlimit( totprice ); if ( m[n].alive ) m[n].countNumGoodsOwned(); } /* re-indexing is done to stop man 1 always being first in line */ index += 1; if (index > nmen) index = 1; // change index findAverages(); displayResults(); day++; redLetterDay = -1; } /* takes an index value and re-indexes it */ public int reindex( int n ) { int i; i = n; if ( n > nmen ) i -= nmen; return( i ); } /* mean idleness = sum (V-C) / L of all available tools */ public double predictMeanIdleness() { double idleness; idleness = m[1].untooledIdleness; /* assume all have same untooled idleness */ for ( int n = 1; n <= ntools; n++ ) { idleness += ( t[n].value - t[n].cost ) / t[n].lifetime; } return( idleness ); } /* predict income of man i */ public double predictIncome( int i ) { int tool; double income = 0.0; if ( trading ) { tool = m[i].shoptool; income = nmen * m[tool].shopPrice / t[tool].lifetime; // income for ( int n = 1; n <= ntools; n++ ) { income -= m[n].shopPrice / t[n].lifetime; // expenditure } } return ( income ); } /* predict idleness of man i */ /* requires m[i].predictedIncome to have been calculated */ /* assumes all tools in use */ public double predictIdleness( int i ) { int n; double idleness; idleness = m[i].untooledIdleness; if ( trading ) { for ( n = 1; n <= ntools; n++ ) { idleness += t[n].value / t[n].lifetime; } idleness -= nmen * t[ m[i].shoptool ].cost / t[ m[i].shoptool ].lifetime; idleness += m[i].predictedIncome; } else { idleness += t[i].value / t[i].lifetime; idleness -= t[i].cost / t[i].lifetime; } return( idleness ); } public void findAverages() { totprice = 0.0; for ( int n = 1; n<= nmen; n++ ) { totprice += m[n].shopPrice; m[n].setavI(); m[n].avIdleness = m[n].getavI() * 100.0 ; } } /* output some results */ public void displayResults() { scrtext.setTextDisplay(); scrtext.textDisplay( thisApplet.t1 ); int maxn = 4; if ( nmen < maxn ) maxn = nmen; for ( int n = 1; n <= maxn; n++ ) { thisApplet.canvas2.nexty[n] = (int)m[n].avIdleness; thisApplet.canvas2.streamLabel[n] = "i" + Integer.toString(n); } double totpurse = 0; for ( int n=1; n <= nmen; n++ ) { totpurse += m[n].purse; }; thisApplet.canvas2.nexty[0] = (int)(totpurse+0.5); thisApplet.canvas2.streamLabel[0] = "$"; thisApplet.canvas2.day = day; thisApplet.canvas2.redLetterDay = redLetterDay; if ( day == nextAvDay ) { nextAvDay += avperiod; } } } /*********** do Choice actions *******************************************/ class ChoiceOptions { public static boolean doChoiceAction( int choice, Economy economy ) { int i, n, a, b; double v; boolean doagain = false; boolean newprice = false; if ( choice == 0 ) { // toggle trading/not trading economy.trading = !economy.trading; for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].trading = !economy.m[n].trading; } } else if ( choice == 1 ) { // set zero prices for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].shopPrice = 0.0; } newprice = true; } else if ( choice == 2) { // C < random price < P for ( n = 1; n <= economy.nmen; n++ ) { i = economy.m[n].shoptool; v = (economy.t[i].value - economy.t[i].cost) * Math.random(); economy.m[n].shopPrice = economy.t[i].cost + v; } newprice = true; } else if ( choice == 3 ) { for ( n = 1; n <= economy.nmen; n++ ) { // set prices = ABS values i = economy.m[n].shoptool; economy.m[n].shopPrice = Math.abs( economy.t[i].value ); } newprice = true; } else if ( choice == 4 ) { // double prices for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].shopPrice *= 2.0; } } else if ( choice == 5 ) { // halve prices for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].shopPrice /= 2.0; } newprice = true; } else if ( choice == 6 ) { // set Prices = Costs for ( n = 1; n <= economy.nmen; n++ ) { i = economy.m[n].shoptool; economy.m[n].shopPrice = economy.t[i].cost; } newprice = true; } else if ( choice == 7 ) { // set Just Prices /* Just Prices to produce equal idleness. prices are related such that Pa = Ca + La.( Pb - Cb ) / Lb where a and b are any two tools, and P is price , C cost, and L lifetime. */ a = economy.m[1].shoptool; // Price1 = justMarkup * Cost1 economy.m[1].shopPrice = economy.t[a].cost * economy.justMarkup; for ( int iter = 0; iter < 3; iter++ ) { for ( n = 1; n <= economy.nmen; n++ ) { i = n + 1; if ( i > economy.nmen ) i = 1; a = economy.m[n].shoptool; b = economy.m[i].shoptool; economy.m[i].shopPrice = economy.t[b].cost + economy.t[b].lifetime * ( economy.m[n].shopPrice - economy.t[a].cost ) / economy.t[a].lifetime; } } newprice = true; } else if ( choice == 8 ) { // set unjust price for man 1 for ( n = 1; n <= economy.nmen; n++ ) { i = economy.m[n].shoptool; if ( n == 1 ) { economy.m[n].shopPrice = economy.t[i].cost + economy.t[i].lifetime * economy.meanIdleness; } else { economy.m[n].shopPrice = economy.t[i].cost; } } newprice = true; } else if ( choice == 9 ) { // set unjust price for man 4 for ( n = 1; n <= economy.nmen; n++ ) { i = economy.m[n].shoptool; if ( n == 4 ) { economy.m[n].shopPrice = economy.t[i].cost + economy.t[i].lifetime * economy.meanIdleness; } else { economy.m[n].shopPrice = economy.t[i].cost; } } newprice = true; } else if ( choice == 10) { // increase untooled idleness for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].untooledIdleness += 0.05; if ( economy.m[n].untooledIdleness > 1.0 ) economy.m[n].untooledIdleness = 1.0; } } else if ( choice == 11) { // decrease untooled idleness economy.ui -= 5; for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].untooledIdleness -= 0.05; } } else if ( choice == 12) { // falling untooled idleness for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].untooledIdleness -= 0.001; doagain = true; // this will be cancelled by any other selection!!! } } else if ( choice == 13) { // limited credit (IOU issue) for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].IOUlimit = economy.totprice; if ( economy.m[n].IOUlimit < economy.m[n].IOUissue ) { economy.m[n].creditworthy = false; } else { economy.m[n].creditworthy = true; } } newprice = true; } else if ( choice == 14) { // unlimited credit (IOU issue) for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].IOUlimit = 0; economy.m[n].creditworthy = true; } } else if ( choice == 15) { // set averaging period to 50 days economy.avperiod = 50; for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].avperiod = 50; for ( i = 0; i < 50; i++ ) { economy.m[n].avI[i] = (int)(economy.m[n].idleness * 100.0 + 0.5); } } } else if ( choice == 16) { // set price = (V+C)/2 for ( n = 1; n <= economy.nmen; n++ ) { i = economy.m[n].shoptool; economy.m[n].shopPrice = ( economy.t[i].value + economy.t[i].cost ) / 2; } newprice = true; } else if ( choice == 17) { // discriminate between tools and luxuries for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].discriminates = true; } } else if ( choice == 18) { // don't discriminate for ( n = 1; n <= economy.nmen; n++ ) { economy.m[n].discriminates = false; } } if ( newprice ) setCreditLimits( economy ); economy.redLetterDay = choice; return( doagain ); } /* find total price of all goods, and set this as credit limit */ public static void setCreditLimits( Economy economy) { double totprice = 0; for ( int n = 1; n <= economy.nmen; n++ ) { totprice += economy.m[n].shopPrice; } for ( int n = 1; n <= economy.nmen; n++ ) { if ( economy.m[n].IOUlimit > 0 ) { economy.m[n].IOUlimit = totprice + 0.1; } } } } /*********** text area formatter *****************************************/ class TextFormatter { public Economy economy; public StringBuffer b; public int rowlen; public int rows = 14; // no. of rows of data to display public final String title[] = { "Man ","Status","%I","%Av.I","%UI","Purse", "Ntools","IOUs","tprice","tstock","tvalue", "tcost ","tlife"," "," " }; public int indent[] = new int[Economy.NMAX]; /* constructor creates Stringbuffer */ public TextFormatter( Economy e ) { int n; String s; economy = e; /* make up a row with blank fields for each man */ b = new StringBuffer(0); for ( n = 0; n <= economy.nmen; n++ ) { indent[n] = 9 * n; // distance to indent b.append(" "); } b.append("\n"); s = b.toString(); rowlen = b.length(); /* add blank rows for each label */ for ( n = 1; n < rows; n++ ) { b.append( s ); } } public void textDisplay( TextArea t ) { t.setText( b.toString() ); } /* make double into 8-char string with leading tab */ public String makeString( double v ) { int decimal, i, digits; boolean leadingzero, debug; char ch; StringBuffer s = new StringBuffer("\t"); v = v + .0005; leadingzero = true; digits = 0; if ( v < 0.0 ) { s.append('-'); digits++; v = Math.abs(v); } for ( double d = 100000; d > .01; d = d * 0.1 ) { decimal = (int)( v / d ); if ( (decimal != 0) || ( d == 1 ) ) leadingzero = false; v = v - (double)decimal * d; // because v % d doesn't work!!! if ( !leadingzero ) { ch = (char)( 48 + decimal ); digits++; s.append( ch ); if ( d == 1.0 ) s.append('.'); } if ( digits > 5 ) break; } while ( s.length() < 8 ) { s.append(' '); } return( s.toString() ); } /* make int into 8-char string with trailing tab */ public String makeString( int v ) { int decimal, i, digits; boolean leadingzero; char ch; StringBuffer s = new StringBuffer("\t"); leadingzero = true; digits = 0; if ( v < 0 ) { s.append('-'); digits++; v = Math.abs(v); } for ( int d = 100000; d > 0; d = d/10 ) { decimal = v / d ; if ( decimal != 0 ) leadingzero = false; v = v % d; if ( !leadingzero || (d == 1) ) { ch = (char)( 48 + decimal ); s.append( ch ); digits++; } if ( digits > 5 ) break; } while ( s.length() < 8 ) { s.append(' '); } return( s.toString() ); } /* put double into StringBuffer field */ public void setField( int man, int field, double v ) { String s; int len, bpos; char ch; s = makeString( v ); len = s.length(); bpos = (field * rowlen) + indent[man]; for ( int i = 0; i < len; i++ ) { ch = s.charAt(i); b.setCharAt(bpos++, ch); } } /* put integer into StringBuffer field */ public void setField( int man, int field, int v ) { String s; int len, bpos; char ch; s = makeString( v ); len = s.length(); bpos = (field * rowlen) + indent[man]; for ( int i = 0; i < len; i++ ) { ch = s.charAt(i); b.setCharAt(bpos++, ch); } } /* put 6-char string into StringBuffer field */ public void setField( int man, int field, String s ) { int len, bpos; char ch; len = s.length(); bpos = (field * rowlen) + indent[man]; for ( int i = 0; i < len; i++ ) { ch = s.charAt(i); b.setCharAt(bpos++, ch); } } /* put all data into StringBuffer fields */ public void setTextDisplay() { int n; for ( n = 0; n <= economy.nmen; n++ ) { if ( n == 0 ) { for ( int i = 0; i < rows; i++ ) { setField( 0, i, title[i] ); } } else { setField( n, 0, n ); if ( economy.m[n].alive ) { setField( n, 1, "\tAlive" ); } else { setField( n, 1, "\tDead "); } setField( n, 2, economy.m[n].idleness * 100.0 ); setField( n, 3, economy.m[n].avIdleness ); setField( n, 4, economy.m[n].untooledIdleness * 100.0 ); setField( n, 5, economy.m[n].purse ); setField( n, 6, economy.m[n].numGoodsOwned ); // if ( economy.m[n].creditworthy ) { setField( n, 6, "\tyes"); } // else { setField( n, 6, "\tno "); } setField( n, 7, economy.m[n].IOUissue ); setField( n, 8, economy.m[n].shopPrice ); setField( n, 9, economy.m[n].shopStock ); setField( n,10, economy.t[economy.m[n].shoptool].value ); setField( n,11, economy.t[economy.m[n].shoptool].cost ); setField( n,12, economy.t[economy.m[n].shoptool].lifetime ); }; } } } /*********** moving graph canvas *****************************************/ class GraphCanvas extends Canvas { Dimension fielddimension; Image fieldImage; Graphics fieldGraphics; int day; int nstreams = 5; int lasty[] = new int[nstreams]; int nexty[] = new int[nstreams]; String streamLabel[] = new String[nstreams]; boolean started; Font font; FontMetrics fm; int redLetterDay; // Constructor allows access to Field objects by this class public GraphCanvas() { super(); for ( int n = 0; n < nstreams; n++ ) { lasty[n] = -100; nexty[n] = -100; streamLabel[n] = " "; } day = 0; started = false; redLetterDay = -1; font = new Font( "Helvetica", Font.PLAIN, 12 ); } public void stop() { fieldGraphics = null; fieldImage = null; } public boolean handleEvent( Event evt ) { return true; } public void redraw() { repaint(); } // update calls paint(), otherwise clears the screen. public void update(Graphics g) { paint(g); } public void paint(Graphics g) { int n; Dimension d = size(); int graph_height = 100; int sideband = 25; int lowband = 15; //Create the offscreen graphics context, if no good one exists. if ( (fieldGraphics == null) ) { fieldImage = createImage(d.width, graph_height + lowband); fieldGraphics = fieldImage.getGraphics(); fieldGraphics.setColor(Color.black); fieldGraphics.fillRect(0, 0, d.width, graph_height + lowband); fieldGraphics.setFont( font ); g.drawImage(fieldImage, 0, 0, this); } fm = fieldGraphics.getFontMetrics( font ); if ( started ) { /* move image 1 pixel leftward */ fieldGraphics.setPaintMode(); fieldGraphics.copyArea(1,0, d.width-sideband, graph_height+lowband-1, -1, 0 ); /* rub out unmoved vertical line */ fieldGraphics.setColor(Color.black); fieldGraphics.fillRect(d.width-sideband-1, 0, d.width-1, graph_height+lowband); /* add righthand text */ fieldGraphics.setColor(Color.lightGray); fieldGraphics.drawString("100", d.width-sideband+1, 10 ); fieldGraphics.drawString("0", d.width-sideband/3-1, graph_height-1 ); /* draw horizontal gridlines */ for ( n = graph_height/4; n <= graph_height; n = n + graph_height/4 ) { fieldGraphics.setColor(Color.darkGray); fieldGraphics.drawLine( d.width-sideband-2, n, d.width-sideband-1, n ); } /* draw vertical gridlines and day numbers */ String s; if ( (day % 100) == 0 ) { fieldGraphics.setColor(Color.lightGray); fieldGraphics.drawLine( d.width-sideband-1, 0, d.width-sideband-1, graph_height ); s = Integer.toString(day); fieldGraphics.setColor(Color.lightGray); fieldGraphics.drawString( s, d.width-sideband-fm.stringWidth(s)-1, graph_height+lowband-1 ); } /* draw red vertical line to show event */ if ( redLetterDay >= 0 ) { fieldGraphics.setColor(Color.red); fieldGraphics.drawLine( d.width-sideband-1, 0, d.width-sideband-1, graph_height ); s = Integer.toString(redLetterDay); fieldGraphics.drawString( s, d.width-sideband-fm.stringWidth(s)-3, fm.getAscent() + 1 ); } /* draw graph lines and labels at right edge of graph */ for ( n = nstreams-1; n >= 0; n-- ) { fieldGraphics.setColor(Color.orange); if ( n==0 ) fieldGraphics.setColor(Color.white); if ( n==1 ) fieldGraphics.setColor(Color.red); if ( n==2 ) fieldGraphics.setColor(Color.green); if ( n==3 ) fieldGraphics.setColor(Color.blue); if ( n==4 ) fieldGraphics.setColor(Color.yellow); fieldGraphics.drawLine( d.width-sideband-2, 100-lasty[n], d.width-sideband-1, 100-nexty[n] ); fieldGraphics.drawString( streamLabel[n], d.width-sideband+2, 100-nexty[n] ); } //Paint the image onto the screen. g.drawImage(fieldImage, 0, 0, this); } else { started = true; fieldGraphics.setColor(Color.black); fieldGraphics.fillRect(0, 0, d.width, graph_height); } for ( n = 0; n < nstreams; n++ ) lasty[n] = nexty[n]; } }