Sunday 28 June 2015

3 Way Battle Simulation

So in my previous post I made reference to a simulation of battles between Mages, Rangers and Warriors. Just thought I'd post it here for anyone to look through and play with! The Barracks.java class is the runnable, and will output a .rep file (simple text document, open with Notepad) with the breakdown of the battle. It wouldn't be too hard to add more info to the report. you can tweak the attack power and defense or any of the other stats for each of the classes.

Mob Class (Parent for the three rpg-classes)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package saving;

import java.io.Serializable;

@SuppressWarnings("serial")
public class Mob implements Serializable{
 
 int maxHealth;
 int health;
 String name;
 int attack;
 double defence;
 double accuracy;
 double dodge;
 String type;
 private String battleReport = null;
 private String attackReport = null;
 
 private boolean isAlive;
 
 public Mob(){
  isAlive = true;
 }
 
 public void takeDamage(int damage){
  int absorb = (int) (damage * defence);
  if(damage - absorb >= 1){   
   health -= (damage - absorb);
  }
  
  battleReport = name +  " absorbs " + absorb + " damage.\n" + name + " takes " + (damage - absorb) + " damage\nHealth remaining: " + health + "\n";
  
  if(health <= 0) {
   health = 0;
   isAlive = false;
  }
 }
 
 public void attack(Mob enemy){
  attackReport = null;
  battleReport = null;
  enemy.attackReport = null;
  enemy.battleReport = null;
  attackReport = (name + " is attacking " + enemy.name);
  if(Math.random() > accuracy){
   // Missed
   attackReport += "\nAttack missed. End of attack.";
   enemy.battleReport = enemy.name + " is unharmed\n";
   return;
  }
  
  if(Math.random() < enemy.dodge){
   // Dodged
   attackReport += "\n" + enemy.name + " dodged the attack. End of attack.\n";
   enemy.battleReport = enemy.name + " is unharmed\n";
   return;
  }
  
  int attackDamage = attack;
  if ((type.equals("Orc")  && enemy.type.equals("Elf")) 
    || (type.equals("Human") && enemy.type.equals("Orc"))
    || (type.equals("Elf") &&  enemy.type.equals("Human"))){
   attackDamage *= 1.2;
   attackDamage -= Math.random() * attackDamage;
  } else if((type.equals("Orc") && enemy.type.equals("Human")) 
    || (type.equals("Human") && enemy.type.equals("Elf"))
    || (type.equals("Elf") && enemy.type.equals("Orc"))){
   attackDamage *= 0.8;
   attackDamage -= Math.random() * attackDamage + 1;
  }
  enemy.takeDamage(attackDamage);
 }
 
 public void heal(int hpUp){
  health += hpUp;
  if(health > maxHealth) health = maxHealth;
 }
 
 public String toString(){
  return "MOB: " + this.hashCode() + "\nHealth: " + health +"\nAlive: " + isAlive; 
 }
 
 public boolean isAlive(){
  return isAlive;
 }
 
 public String getBattleReport(){
  return battleReport;
 }
 
 public String getAttackReport(){
  return attackReport;
 }

}

Main Class - Barracks.java

Edit the COMBATANTS constant to change how many mobs are in each army, note, it will take a lot longer to run if you go too high!

1000 combatants (3,000 total) took approx 0.5 seconds
10,000 combatants (30,000 total) took approx 5 seconds
100,000 combatants (300,000 total) took approx 62 seconds
1,000,000 Could not run, Out of Memory.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package saving;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;

public class Barracks {

 private final int COMBATANTS = 500; // exceed 100,000 at your own risk.
 private int skirmishes;
 
 @SuppressWarnings("rawtypes")
 private ArrayList<ArrayList> mobs;
 
 public static void main(String[] args) {
  Barracks b = new Barracks();
  b.go();
 }
 
 public void go(){
  long startTime = System.currentTimeMillis();
  System.out.println("Starting simulation: Number of Combatants per army = " + COMBATANTS);
  // Armies of Mobs
  ArrayList<Orc> orcs = new ArrayList<Orc>();
  ArrayList<Elf> elves = new ArrayList<Elf>();
  ArrayList<Human> humans = new ArrayList<Human>();
  
  // Populate armies
  for(int i = 0; i < COMBATANTS; i++){
   orcs.add(new Orc("Warrior" + i));
   elves.add(new Elf("Ranger" + i));
   humans.add(new Human("Mage" + i));
  }

  // Armies added to array list
  mobs = new ArrayList<>();
  mobs.add(orcs);
  mobs.add(elves);
  mobs.add(humans);
  
  try {
   // Create text report
   FileWriter fw = new FileWriter("BattleReport.rep");
   BufferedWriter bw = new BufferedWriter(fw);
   // Report title
   bw.write("Battle between " + COMBATANTS + " orcs, elves and Humans:");
   
   // Main simulation loop
   while(!orcs.isEmpty() && !elves.isEmpty() && !humans.isEmpty()){
    // While all armies have combatants, select to opposite faction mobs
    int class1 = (int) (Math.random() * 3);
    int mobNum = (int) (Math.random() * mobs.get(class1).size());
    Mob fighter1 = (Mob) mobs.get(class1).get(mobNum);
    
    // Prevent same faction skirmish
    int class2 = (int) (Math.random() * 3);
    while(class2 == class1){
     class2 = (int) (Math.random() * 3);
    }
    int mob2Num = (int)(Math.random() * mobs.get(class2).size());    
    Mob fighter2 = (Mob) mobs.get(class2).get(mob2Num);
    
    // Actual skirmish
    fighter1.attack(fighter2);
    // Get report on actions taken
    bw.write("\n" + fighter1.getAttackReport());
    // Get report on hits taken and damage mitigation
    bw.write("\n" + fighter2.getBattleReport());
    // If damage is taken, increment skirmishes counter
    if(fighter2.getBattleReport().contains("takes")){
     skirmishes++;
    }
    if(!((Mob) mobs.get(class1).get(mobNum)).isAlive()){
     mobs.get(class1).remove(mobNum);
    } else if(!((Mob) mobs.get(class2).get(mob2Num)).isAlive()){
     mobs.get(class2).remove(mob2Num);
    }
    
   }
   long delta = System.currentTimeMillis() - startTime;
   double elapsedTime = delta / 1000.0;
   System.out.println("Ending simulation: Number of Skirmishes = " + skirmishes + "\nTime taken: " + elapsedTime + "sec");
   
   // Write out summary of battles
   bw.write(battleReport());
   bw.close();
   
  }catch (IOException e){
   
  }
  
 }
 
 public String battleReport(){
  String report = null;
  // Find defeated army and display remaining enemies and total skirmishes
  if(mobs.get(0).isEmpty()){
   report = "\nOrcs are defeated" +
   "\n" + mobs.get(1).size() + " Elves remain" +
   "\n" + mobs.get(2).size() + " Humans remain";
  } else if (mobs.get(1).isEmpty()){
   report = "\nElves are defeated" +
     "\n" + mobs.get(0).size() + " Orcs remain" +
     "\n" + mobs.get(2).size() + " Humans remain";
  }else if (mobs.get(2).isEmpty()){
   report = "\nHumans are defeated" +
     "\n" + mobs.get(0).size() + " Orcs remain" +
     "\n" + mobs.get(1).size() + " Elves remain";
  }

  return (report + "\n" + skirmishes + " total Skirmishes");
 }
}

Elf.java - The Ranger class

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package saving;

@SuppressWarnings("serial")
public class Elf extends Mob{
 
 public Elf(String name){
  super();
  this.name = name;
  maxHealth = 180;
  health = maxHealth;
  type = "Elf";
  
  attack = 16;
  defence = 0.15;
  accuracy = 0.95;
  dodge = 0.06;
 }
}

Orc.java - The Warrior class


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package saving;

@SuppressWarnings("serial")
public class Orc extends Mob{
 
 public Orc(String name){
  super();
  this.name = name;
  maxHealth = 150;
  health = maxHealth;
  type = "Orc";
  
  attack = 20;
  defence = 0.30;
  accuracy = 0.85;
  dodge = 0.1;
 }
}

Mage.java - The Mage class


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package saving;

@SuppressWarnings("serial")
public class Human extends Mob{

 public Human(String name){
  super();
  this.name = name;
  maxHealth = 140;
  health = maxHealth;
  type = "Human";
  
  attack = 30;
  defence = 0.12;
  accuracy = 0.90;
  dodge = 0.05;
 }
 
}

No comments:

Post a Comment