001         package com.croftsoft.apps.dice;
002    
003         import java.awt.Graphics;
004         import java.awt.*;
005         import java.util.*;
006         import java.lang.Math;
007         import java.awt.Color;
008         import java.awt.Point;
009         import java.awt.Rectangle;
010         import java.util.*;
011         import javax.swing.*;
012    
013         import com.croftsoft.core.lang.lifecycle.Lifecycle;
014         import com.croftsoft.core.animation.*;
015         import com.croftsoft.core.animation.component.*;
016    
017         /*********************************************************************
018         * Artificial neural network perceptron demonstration.
019         *
020         * @version
021         *   2002-03-23
022         * @since
023         *   1996-08-23
024         * @author
025         *   <a href="https://www.croftsoft.com/">David Wallace Croft</a>
026         *********************************************************************/
027    
028         public class  Dice
029           extends JApplet
030           implements Lifecycle, ComponentAnimator
031         //////////////////////////////////////////////////////////////////////
032         //////////////////////////////////////////////////////////////////////
033         {
034    
035         private static final String  VERSION = "2002-03-13";
036    
037         private static final String  APPLET_TITLE
038           = "Dice";
039    
040         private static final String  INFO
041           = "\n" + APPLET_TITLE + "\n"
042           + "Copyright 2002 CroftSoft Inc\n"
043           + "https://www.croftsoft.com/\n"
044           + "Version " + VERSION + "\n";
045    
046         private static final int  FRAME_RATE = 24;
047    
048         private static final int  text_x = 10; 
049         private static final int  text_y = 20;  // distance between lines
050    
051         //
052    
053         private AnimatedComponent  animatedComponent;
054    
055         private Rectangle          bounds = new Rectangle ( );
056    
057         //
058    
059         private int start_fights = 2000;
060         private int fights_max   = 30;
061    
062         private Random  random = new Random ( );
063    
064         private long damage;
065         private long count;
066         private long sum;
067         private double mean;
068    
069         private NPC npc1 = new NPC ( "Combatant_1" );
070         private NPC npc2 = new NPC ( "Combatant_2" );
071    
072         private int fights = 0;
073         private int fights_finished = 0;
074         private long round_num = 0;
075    
076         private long hp_max = 100;
077    
078         private Point    fight_history   [ ] = new Point   [ fights_max ];
079         private boolean  winloss_history [ ] = new boolean [ fights_max ];
080    
081         private Rectangle  r = new Rectangle ( text_x, 8 * text_y, 200, 200 );
082    
083         private double   w_ac          = 100.0;
084         private double   w_hp          = 20.0;
085         private double   bias          = 1000.0;
086         private double   w_bias        = -50.0 * 20.0 / bias;
087         private double   learn_rate    = 1.0;
088    //   private double   win_prob      = 0.5;
089         private boolean  win_predicted = true;
090    
091         //////////////////////////////////////////////////////////////////////
092         //////////////////////////////////////////////////////////////////////
093    
094         public String  getAppletInfo ( ) { return INFO; }
095    
096         //////////////////////////////////////////////////////////////////////
097         //////////////////////////////////////////////////////////////////////
098    
099         public synchronized void  init ( )
100         //////////////////////////////////////////////////////////////////////
101         {
102           System.out.println ( INFO );
103    
104           animatedComponent = new AnimatedComponent ( this, FRAME_RATE );
105    
106           animatedComponent.init ( );
107    
108           setContentPane ( animatedComponent );
109    
110           npc2.combat_stats.ac = 0;
111    
112           npc2.combat_stats.hp = 50;
113    
114           update_fight_history ( npc2.combat_stats.ac, npc2.combat_stats.hp );
115         }
116    
117         public synchronized void  start ( )
118         //////////////////////////////////////////////////////////////////////
119         {
120           animatedComponent.start ( );
121         }
122    
123         public synchronized void  stop ( )
124         //////////////////////////////////////////////////////////////////////
125         {
126           animatedComponent.stop ( );
127         }
128    
129         public synchronized void  destroy ( )
130         //////////////////////////////////////////////////////////////////////
131         {
132           animatedComponent.destroy ( );
133         }
134    
135         //////////////////////////////////////////////////////////////////////
136         //////////////////////////////////////////////////////////////////////
137    
138         public void  update ( JComponent  component )
139         //////////////////////////////////////////////////////////////////////
140         {
141           fight ( );
142    
143           component.repaint ( );
144         }
145    
146         public void  paint (
147           JComponent  component,
148           Graphics2D  graphics )
149         //////////////////////////////////////////////////////////////////////
150         {
151           component.getBounds ( bounds );
152    
153           graphics.setColor ( Color.white );
154    
155           graphics.fillRect (
156             bounds.x, bounds.y, bounds.width, bounds.height );
157    
158           graphics.setColor ( Color.black );
159    
160           graphics.drawString ( "Round " + round_num, text_x,     text_y );
161    
162           graphics.drawString ( npc1.toString ( )   , text_x, 2 * text_y );
163    
164           graphics.drawString ( npc2.toString ( )   , text_x, 3 * text_y );
165    
166           graphics.drawString (
167             "Y = W_AC * AC + W_HP * HP + W_BIAS * BIAS", text_x, 4 * text_y );
168    
169           double  weighted_sum = w_ac * fight_history [ fights - 1 ].x
170             + w_hp * fight_history [ fights - 1 ].y + w_bias * bias;
171    
172           graphics.drawString ( "  = " + w_ac + " * " + fight_history [ fights - 1 ].x
173             + " + " + w_hp + " * " + fight_history [ fights - 1 ].y
174             + " + " + w_bias + " * " + bias + " = " + weighted_sum, text_x, 5 * text_y );
175    
176           if ( round_num == 0 )
177           {
178             graphics.drawString ( "Combat begins.", text_x, 6 * text_y );
179           }
180           else if ( ( npc1.combat_stats.hp > 0 ) && ( npc2.combat_stats.hp <= 0 ) )
181           {
182             graphics.drawString ( npc1.name + " wins!", text_x, 6 * text_y );
183           }
184           else if ( ( npc2.combat_stats.hp > 0 ) && ( npc1.combat_stats.hp <= 0 ) )
185           {
186             graphics.drawString ( npc2.name + " wins!", text_x, 6 * text_y );
187           }
188           else
189           {
190             graphics.drawString ( "Combat continues.", text_x, 6 * text_y );
191           }
192    
193           graphics.drawString ( "Win predicted:  " + win_predicted, text_x, 7 * text_y );
194    
195    //     graphics.drawString ( "Win confidence:  " + win_prob, text_x, 7 * text_y );
196    
197           graphics.drawRect ( r.x, r.y, r.width, r.height );
198    
199           plot_Line ( -w_ac / w_hp, -w_bias * bias / w_hp,
200             r, graphics, -10, 10, 0, hp_max );
201    
202           plot_ac_hp ( r, graphics );
203         }
204    
205         public void update_fight_history ( long ac, long hp )
206         //////////////////////////////////////////////////////////////////////
207         {
208           if ( fights == fights_max ) {
209             for ( int index_fight = 0; index_fight < fights - 1; index_fight++ ) {
210               fight_history [ index_fight ] = fight_history [ index_fight + 1 ];
211               winloss_history [ index_fight ]
212                 = winloss_history [ index_fight + 1 ];
213             }
214           } else {
215             fights++;
216           }
217           fight_history [ fights - 1 ] = new Point ( ( int ) ac, ( int ) hp );
218         }
219    
220         public void fight ( ) {
221         //////////////////////////////////////////////////////////////////////
222           if ( ( npc1.combat_stats.hp > 0 ) && ( npc2.combat_stats.hp <= 0 ) ) {
223             update_winloss_history ( true );
224    //         paint_buffered ( );
225             npc1 = new NPC ( "Combatant_1", npc1.xp + 1 );
226             npc2 = new NPC ( "Combatant_2", npc2.xp );
227             prep_next_fight ( );
228           } else if ( ( npc2.combat_stats.hp > 0 ) && ( npc1.combat_stats.hp <= 0 ) ) {
229             update_winloss_history ( false );
230    //         paint_buffered ( );
231             npc1 = new NPC ( "Combatant_1", npc1.xp );
232             npc2 = new NPC ( "Combatant_2", npc2.xp + 1 );
233             prep_next_fight ( );
234           } else if ( round_num >= 0 ) {
235             round ( npc1.combat_stats, npc2.combat_stats );
236           }
237           round_num++;
238    /*
239           if ( round_num >= 0 ) {
240             paint_buffered ( );
241           }
242    */
243         }
244    
245         public boolean attacker_hits (
246           long attacker_adjusted_thac0,
247           long defender_adjusted_AC ) {
248         //////////////////////////////////////////////////////////////////////
249           long to_hit_roll = roll ( 1, 20, 0 );
250           if ( to_hit_roll == 20 ) return true;
251           if ( attacker_adjusted_thac0 - to_hit_roll <= defender_adjusted_AC )
252             return true;
253           return false;
254         }
255    
256         public long attack (
257           Combat_Stats  attacker_Combat_Stats,
258           Combat_Stats  defender_Combat_Stats ) {
259         //////////////////////////////////////////////////////////////////////
260         // Returns the damage for the attack (0 if a miss).
261         //////////////////////////////////////////////////////////////////////
262           if ( !attacker_hits (
263             attacker_Combat_Stats.thac0,
264             defender_Combat_Stats.ac ) ) return 0;
265           return roll ( attacker_Combat_Stats.damage_multiplier,
266                         attacker_Combat_Stats.damage_base,
267                         attacker_Combat_Stats.damage_bonus );
268         }
269    
270         public void plot_ac_hp ( Rectangle  r, Graphics  g ) {
271         //////////////////////////////////////////////////////////////////////
272           Color  winloss_color;
273         //////////////////////////////////////////////////////////////////////
274           for ( int index_fight = 0; index_fight < fights - 1; index_fight++ ) {
275             if ( winloss_history [ index_fight ] )
276               winloss_color = java.awt.Color.green;
277             else
278               winloss_color = java.awt.Color.red;
279             plot_xy ( winloss_color,
280               ( double ) fight_history [ index_fight ].x,
281               ( double ) fight_history [ index_fight ].y,
282               r, g, -10, 10, 0, hp_max );
283           }
284           plot_xy ( java.awt.Color.blue,
285               ( double ) fight_history [ fights - 1 ].x,
286               ( double ) fight_history [ fights - 1 ].y,
287               r, g, -10, 10, 1, 100 );
288         }
289    
290         public void  plot_Line (
291           double     m,
292           double     b,
293           Rectangle  r,
294           Graphics   g,
295           double     x0,
296           double     x1,
297           double     y0,
298           double     y1 ) {
299         //////////////////////////////////////////////////////////////////////
300           double  x_scale = ( double ) r.width  / ( double ) ( x1 - x0 );
301           double  y_scale = ( double ) r.height / ( double ) ( y1 - y0 );
302         //////////////////////////////////////////////////////////////////////
303           g.drawLine ( r.x,
304             r.y + r.height - ( int ) ( ( m * x0 + b - y0 ) * y_scale ),
305             r.x + r.width ,
306             r.y + r.height - ( int ) ( ( m * x1 + b - y0 ) * y_scale ) );
307         }
308    
309         public void  plot_xy (
310           Color      c,
311           double     x,
312           double     y,
313           Rectangle  r,
314           Graphics   g,
315           double     x0,
316           double     x1,
317           double     y0,
318           double     y1 ) {
319         //////////////////////////////////////////////////////////////////////
320           double  x_scale = ( double ) r.width  / ( double ) ( x1 - x0 );
321           double  y_scale = ( double ) r.height / ( double ) ( y1 - y0 );
322           double  oval_size = ( x_scale < y_scale ) ? x_scale : y_scale;
323           oval_size = ( oval_size > 4 ) ? oval_size : 4;
324    
325           Color  c_old = g.getColor ( );
326           g.setColor ( c );
327           g.fillOval ( r.x + ( int ) ( ( x - x0 ) * x_scale ),
328             r.y + r.height - ( int ) ( ( y - y0 ) * y_scale ),
329             ( int ) oval_size, ( int ) oval_size );
330           g.setColor ( c_old );
331         }
332    
333         public void  prep_next_fight ( ) {
334         //////////////////////////////////////////////////////////////////////
335           npc2.combat_stats.ac = roll ( 1, 21, -11 );
336           npc2.combat_stats.hp = roll ( 1, hp_max, 0  );
337           update_fight_history ( npc2.combat_stats.ac, npc2.combat_stats.hp );
338    //       win_prob = sigmoid ( ( w_ac * npc2.combat_stats.ac
339    //         + w_hp * npc2.combat_stats.hp + w_bias * bias ) / 1000.0 );
340           if ( w_ac * npc2.combat_stats.ac
341             + w_hp * npc2.combat_stats.hp + w_bias * bias >= 0.0 )
342             win_predicted = true;
343           else
344             win_predicted = false;
345           round_num = -1;
346         }
347    
348         public long roll (
349           long multiplier,
350           long base,
351           long offset ) {
352         //////////////////////////////////////////////////////////////////////
353           long temp = 0;
354         //////////////////////////////////////////////////////////////////////
355           for ( long index_roll = 0; index_roll < multiplier; index_roll++ ) {
356             temp += 1 + Math.round (
357               ( double ) ( base - 1 ) * random.nextDouble ( ) );
358           }
359           return temp + offset;
360         }
361    
362         public boolean round (
363           Combat_Stats combatant_1_Combat_Stats,
364           Combat_Stats combatant_2_Combat_Stats ) {
365         //////////////////////////////////////////////////////////////////////
366         // Initiative 50%/50% chance for each.
367         // Returns true if both combatants are still up.
368         //////////////////////////////////////////////////////////////////////
369           if ( roll ( 1, 2, 0 ) == 1 ) {
370             if ( combatant_1_Combat_Stats.hp > 0 ) {
371               combatant_2_Combat_Stats.hp
372                 -= attack ( combatant_1_Combat_Stats, combatant_2_Combat_Stats );
373             }
374             if ( combatant_2_Combat_Stats.hp > 0 ) {
375               combatant_1_Combat_Stats.hp
376                 -= attack ( combatant_2_Combat_Stats, combatant_1_Combat_Stats );
377             }
378           } else {
379             if ( combatant_2_Combat_Stats.hp > 0 ) {
380               combatant_1_Combat_Stats.hp
381                 -= attack ( combatant_2_Combat_Stats, combatant_1_Combat_Stats );
382             }
383             if ( combatant_1_Combat_Stats.hp > 0 ) {
384               combatant_2_Combat_Stats.hp
385                 -= attack ( combatant_1_Combat_Stats, combatant_2_Combat_Stats );
386             }
387           }
388           return ( ( combatant_1_Combat_Stats.hp > 0 )
389                 && ( combatant_2_Combat_Stats.hp > 0 ) );
390         }
391    
392    //     public double  sigmoid ( double  a ) {
393    //     //////////////////////////////////////////////////////////////////////
394    //       return 1.0 / ( 1.0 + java.lang.Math.exp ( -a ) );
395    //     }
396    
397         public void update_winloss_history ( boolean  is_win ) {
398         //////////////////////////////////////////////////////////////////////
399           double  delta;
400         //////////////////////////////////////////////////////////////////////
401           winloss_history [ fights - 1 ] = is_win;
402    //       if ( is_win ) delta = -learn_rate * ( win_prob - 1.0 ) * ( win_prob ) * ( 1.0 - win_prob );
403    //         else        delta = -learn_rate * ( win_prob - 0.0 ) * ( win_prob ) * ( 1.0 - win_prob );
404           if ( is_win && !win_predicted ) {
405             delta = learn_rate;
406           } else if ( !is_win && win_predicted ) {
407             delta = -learn_rate;
408           } else
409             delta = 0.0;
410           fights_finished++;
411           delta = delta / fights_finished;
412           w_ac   += delta * ( double ) fight_history [ fights - 1 ].x;
413           w_hp   += delta * ( double ) fight_history [ fights - 1 ].y;
414           w_bias += delta * bias;
415         }
416    
417         //////////////////////////////////////////////////////////////////////
418         //////////////////////////////////////////////////////////////////////
419         }