# calculations for values that match specified criteria in a limit search space

I'm developing a RPG game and being blocked by the settings of HP(hit points/health points/health power), attack and defense for the characters - Assassin, Tank and Warrior

I'd like to figure out the values so that the Assassin is guaranteed to defeat the Warrior, meanwhile, the Warrior is guaranteed to defeat the Tank whereas the Tank is guaranteed to defeat the Assassin.

So, I wrote a lab to solve this puzzle.

Here is part of Character.java, the rest of it are just auto generated setters and getters.

private int maxHp, hp, attack, defense, level;
private int place, xp, gold;
private String name;

public void setHp(int hp) {
if (hp>maxHp){
this.hp = maxHp;
} else {
if(hp<0){
this.hp = 0;
} else {
this.hp = hp;
}
}
}

public Character(String name, int maxHp, int attack, int defense){
this(name, maxHp, attack, defense, 0, 0, 1);
}

public Character(String name, int maxHp, int attack, int defense, int xp, int gold, int level) {
//        super(); // Is it necessary to call super() here?
this.name = name;
this.maxHp = maxHp;
this.hp = maxHp;
this.attack = attack;
this.defense = defense;
this.xp = xp;
this.gold = gold;
this.level = level;
}

public String toString(){
return name + "'s maxHP: " + maxHp + " Attack: " + attack + " Defense: " + defense;
}


The Util class is just a shorthand for System.out.println()

public class Util {
public static void pln(){
System.out.println();
}
public static void pln(Object obj) {
System.out.println(obj);
}
public static void pln(int value) {
System.out.println(value);
}
}


LabMain does the calculation which contains a multi-layer loop.

class LabMain {
final static int MINHP = 1;
final static int MAXHP = 100;
final static int MINATTACK = 1;
final static int MINDEFENSE = 0;

public static boolean isBattleFinished(Character player, Character opponent,
int dmgDealt, int dmgTook) {

// the player will never win
// as they cannot deal any damage to their opponent
if (dmgDealt <= 0) {
player.setHp(0);
return true;
}

if (dmgTook < 0)
dmgTook = 0;

opponent.setHp(opponent.getHp() - dmgDealt);
player.setHp(player.getHp() - dmgTook);

if (player.getHp() <= 0) {
return true;
} else if (opponent.getHp() <= 0) {
return true;
}

return false;
}

public static int didTheyWin(Character player, Character opponent){
int dmgDealt = player.getAttack() - opponent.getDefense();
int dmgTook = opponent.getAttack() - player.getDefense();
int round = 0;
boolean isFinished = true;

do {
round++;
isFinished = isBattleFinished(player, opponent, dmgDealt, dmgTook);
} while (round<MAXHP && !isFinished);

if (player.getHp() > 0) {
return round;
}

return 0;
}

static void lab() {
for(int assassinMaxHP = MINHP; assassinMaxHP < MAXHP; assassinMaxHP+=MINHP){
Util.pln("assassinMaxHP: " + assassinMaxHP);
for(int assassinAttack = MINATTACK; assassinAttack < MAXHP; assassinAttack+=MINHP){
for(int assassinDefense = MINDEFENSE; assassinDefense < MAXHP; assassinDefense+=MINHP){
Util.pln("assassinDefense: " + assassinDefense);
for(int warriorMaxHP = MINHP; warriorMaxHP < MAXHP; warriorMaxHP+=MINHP){
Util.pln("warriorMaxHP: " + warriorMaxHP);
for(int warriorAttack = MINATTACK; warriorAttack < MAXHP; warriorAttack+=MINHP){
Util.pln( "assassinAttack: " + assassinAttack +
" assassinDefense:" + assassinDefense +
" warriorMaxHP: " + warriorMaxHP +
" warriorAttack: " + warriorAttack);
for(int warriorDefense = MINDEFENSE; warriorDefense < MAXHP; warriorDefense+=MINHP){
for(int tankMaxHP = MINHP; tankMaxHP < MAXHP; tankMaxHP+=MINHP){
for(int tankAttack = MINATTACK; tankAttack < MAXHP; tankAttack+=MINHP){
for(int tankDefense = MINDEFENSE; tankDefense < MAXHP; tankDefense+=MINHP){
Character assassin1 = new Character("A", assassinMaxHP, assassinAttack, assassinDefense);
Character warrior1 = new Character("W", warriorMaxHP, warriorAttack, warriorDefense);
if(didTheyWin(assassin1, warrior1)>0){
Character warrior2 = new Character("W", warriorMaxHP, warriorAttack, warriorDefense);
Character tank2 = new Character("T", tankMaxHP, tankAttack, tankDefense);
if(didTheyWin(warrior2, tank2)>0){
Character tank3 = new Character("T", tankMaxHP, tankAttack, tankDefense);
Character assassin3 = new Character("A", assassinMaxHP, assassinAttack, assassinDefense);
if(didTheyWin(tank3, assassin3)>0){
Util.pln("- - -");
Util.pln(assassin1);
Util.pln(warrior1);
Util.pln(tank2);
Util.pln("- - -");
System.exit(0);
}
}
}
}
}
}
}
}
}
}
}
}
}

public static void main(String[] args) {
lab();
}
}


I put the full code on https://github.com/albert10jp/javamud

Here is the first finding

A's maxHP: 1 Attack: 1 Defense: 1
W's maxHP: 5 Attack: 1 Defense: 0
T's maxHP: 2 Attack: 2 Defense: 0


Although W is more like the Tank whereas T is more like the Warrior :)

Please ignore the bunch of Util.pln inside lab() temporarily which is just an indicator to tell me my code is not in an infinite loop and I'm woking on improving it.

Questions:

1.Is there a more elegant way that I don't have to do so many layers of loops?

2.Is it necessary to call super() in the constructor of Character?

Any other comments and suggestions are also appreciated.

I would like to mention that this (T > A > W > T) is a very interesting problem to figure out.

That being said, let me provide my inputs.

1. Actually super call is necessary when your class extends another class. Why so? Because this helps you call the parent classes constructor and initialize the parent class correctly.

2. Moving onto the GameLogic class that you have in git but not asked for review.

Functions checkAct and battle functions do some work bases on act. Now, this if-else ladder can be improved by changing this to switch block. You might want to clean that up.

switch(act) {
case 1:
...
break;
...
default:
...
break;
}

3. Now the final function lab. It is unnecessarily looping over tank if if(didTheyWin(assassin1, warrior1)>0) is false i.e. you can skip whatever tank values you get.

Original:

for (assassin)
for (warrior)
for (tank)
if (assassin > warrior)
if (warrior > tank)
if (tank > assassin)
exit


becomes:

for (assassin)
for (warrior)
if (assassin > warrior)
for (tank)
if (warrior > tank)
if (tank > assassin)
exit


breaking the problem even further:

for (assassin)
for (warrior)
if (assassin > warrior)
collect in set of assassin and warrior

for (warrior)
if (warrior not in set warrior)     // ignore warriors and don't bother for tank
continue
for (tank)
if (warrior > tank)
collect in set tank
else
remove from set warrior     // this is not desired.

for (tank)
if (tank not in set tank)           // ignore tanks and don't bother for assassin
continue
for (assassin)
if (assassin not in set assassin)   // ignore assassin
if (tank > assassin)
exit / continue
else
remove from set assassin    // this is not desired


Now the final values remaining in all the sets will be your result.

Hope this helps.

• Thank you so much. Does "collect in set of assassin and warrior" mean to put all the combinations of assassins and warriors that the Assassin beats the Warrior in a list of array, which might be a huge a list? May 24, 2021 at 5:39
• first i meant separate sets for assassin and warrior. then if you have copy constructor than store objects and later match those objects down the line. else store values of HP/attack/defense and later match the values in respective loop to skip rest of the loops. May 24, 2021 at 8:15