Hulk Uses Smash – It’s Super Effective

Reading Time: 6 minutes
Image by: Dylan Slavey, used with permission
Image by: Dylan Slavey, used with permission

I was at the kitchen table, poring over the day’s mail, when my son relayed one of those interesting “internet facts” to me.

“Did you know that, for every year a person ages, their attention span increases by approx…are we out of Cookie Crisp?”

A short attention span is one of the toughest roadblocks an instructor encounters when teaching children complex skills. Unfortunately, as parents, we often exacerbate this with our own impatience. We can be so eager for our kids to know what we know, to be able to do what we can do, we forget how much joy is in the exploration and experimentation of learning, what it was like to literally know nothing about our subject matter, when every new song, chord, stroke, or command discovered was a joyous accomplishment. We don’t allow them to try and fail, arguably the best learning method, because we’re busy pushing them to the next level, cramming as much “knowledge” into them as possible, and in the process, usually boring them out of their minds.

Keeping a child focused is one thing; just getting them into the room is quite another. Be prepared to step out of your comfort zone. Eine kleine Nachtmusik might be one of the most beautiful pieces of music ever written, but for a child learning the piano, it doesn’t hold a candle to Lady Gaga or Train. They want to learn something that reflects their interests, not those of a skilled pianist. And that’s ok. There will be plenty of time later to appreciate the genius of Mozart; let them have fun with Miley Cyrus today. Or, in our case, forget Bellman-Ford and let’s focus on Hulk and Thor. Specifically, who would win in a fight.

In keeping with the spirit of iterative learning, we’re going to start with a very simple battle program. This is something a child can sit down and knock out in a few minutes, but also come back to later, on their own, and poke around on. At its heart, our program is one simple if…then statement:


if (Hulk > Thor)
{
     Hulk wins
}
else if (Thor > Hulk)
{
     Thor wins
}
else
{
     It's a draw
}

Nothing complex there, even if it is worthy of at least an hour’s debate on Asgardian DNA vs. gamma radiation. The real question is, “What do we mean by ‘greater than’?” That’s such an arbitrary idea when it comes to superheroes, we need to define some attributes. What makes a superhero super? Here are five basic attributes to start with:

  • Strength – the power of the attack
  • Defense – the ability to weather the attack
  • Agility – the ability to avoid the attack
  • Speed – the frequency of the attack
  • Stamina – how long they can continue fighting

So, for example, we might use a 1-100 scale and define our heroes as follows:

StrengthDefenseAgilitySpeedStamina
Hulk999952095
Thor8585404085

Great, we now have our heroes ready to fight. Do we just add up their numbers and the highest number wins every time? Of course not! We haven’t even written any code yet. What kind of programming tutorial would that be?

The First Rule of Superhero Fight Club

Here are the rules for our battle. You can modify these as you see fit.

  1. Every fighter starts with 20 HP.
  2. Fighters will battle until one is defeated (HP = 0) or they get too tired (stamina = 0).
  3. Fighters’ attack and defense powers will be determined by using random numbers, their attribute, and the round number. E.g. Hulk has strength of 99 and it is currently round 7. Hulk’s attack power will be a random number between (99 – 7) and 99.
  4. Fighters’ attack frequency will be based on their Speed attribute. In our example above, Thor would attack twice for every one time Hulk attacks.
  5. Fighters’ ability to dodge an attack will be determined based on the Speed of the attacker (AS), the defender’s Agility (DA), and two random numbers (R1, R2) as follows: If R1, where R1 is a random number between 0 and AS, is greater than R2, where R2 is a random number between 0 and DA, the attack was successful. Otherwise, the attacker misses.

On With the Code, This Is It

First, we’ll create a couple of classes to store our superhero information as well as our fighter information. We will use Hero to define our superhero and Fighter to track their progress during the current battle.


class Fighter
   {
      internal Hero Hero { get; set; }
      internal double Power { get; set; }
      internal int AttackCount { get; set; }
   }

class Hero
   {
      internal string Name { get; set; }
      internal int Strength { get; set; }
      internal int Speed { get; set; }
      internal int Agility { get; set; }
      internal int Defense { get; set; }
      internal int Stamina { get; set; }
   }

We’ll play fair and make it a one-on-one battle:


   var Hulk = new Hero { Name = "Hulk", Strength = 99, Speed = 20, Agility = 5, Defense = 99, Stamina = 95 };
   var Thor = new Hero { Name = "Thor", Strength = 85, Speed = 40, Agility = 40, Defense = 85, Stamina = 85 };
            
   List fighters = new List();
   fighters.Add(new Fighter { Hero = Hulk, Power = 20, AttackCount = Hulk.Speed });
   fighters.Add(new Fighter { Hero = Thor, Power = 20, AttackCount = Thor.Speed });

We’ll keep track of what round we’re in, as well as initialize our random number generator (RNG).


   int rounds = 0;
   Random diceRoll = new Random();

Hajime!


//keep doing this until someone is defeated
while (fighters.All(f => f.Power > 0))
{
   //let's figure out who's attacking this round, based on Speed
   Fighter attacker = fighters.OrderByDescending(f => f.AttackCount).FirstOrDefault();
   Fighter defender = fighters.OrderByDescending(f => f.AttackCount).Last();

   //up the defender's attack count
   defender.AttackCount += defender.Hero.Speed;

   //how Powerful is the attack (Strength of attacker)?
   var attackerStrength = diceRoll.Next(attacker.Hero.Strength - round, attacker.Hero.Strength);

   //how likely is the attack to land (Agility of defender)?
   var dodge = diceRoll.Next(0, attacker.Hero.Speed) < diceRoll.Next(0, defender.Hero.Agility);

   //how damaging is the attack (Defense of defender)?
   var defenderDefense = diceRoll.Next(defender.Hero.Defense - round, defender.Hero.Defense);

   //did we dodge? if not, calculate damage
   if (!dodge)
   {
      var damage = attackerStrength > defenderDefense ? attackerStrength - defenderDefense : 0;
      defender.Power -= damage;
      Console.WriteLine( "{0} attacks {1}! {2} damage done." , attacker.Hero.Name, defender.Hero.Name, damage);
   }
   else
   {
      Console.WriteLine( "{0} Misses!" , attacker.Hero.Name);
   }

   //what's the score?
   Console.WriteLine( "{0}({1}) | {2}({3})" , fighters[0].Hero.Name, fighters[0].Power.ToString("0.0" ), fighters[1].Hero.Name, fighters[1].Power.ToString("0.0" ));
   
   //on to the next round...unless someone is too tired, that is
   round++;
   if (round > fighters.Min(f => f.Hero.Stamina))
   {
      break;
   }
}

…And The Winner Is

Nothing left to do but tell us who won.


//the fight is over
if (round > fighters[0].Hero.Stamina)
{
   throw new Exception( String.Format( "{0} is too tired to continue. {1} wins!", fighters[0].Hero.Name, fighters[1].Hero.Name));
}
else if (round > fighters[1].Hero.Stamina)
{
   throw new Exception( String.Format( "{0} is too tired to continue. {1} wins!", fighters[1].Hero.Name, fighters[0].Hero.Name));
}
else
{
   Console.WriteLine( "The fight is over" );
   Console.WriteLine( "The winner is: {0}" , fighters.OrderByDescending(f => f.Power).FirstOrDefault().Hero.Name);
}

As you run through battles, change up the properties of your heroes to more closely match reality, or even add new characters. In my example above, Thor always won because Hulk could never get a shot in. I realized that I either greatly overestimated Thor’s ability to dodge or underestimated Hulk’s speed.

Stay tuned for Part II, where we learn how to store and retrieve character information as well as battle results.

The complete code:


using System;
using System.Collections.Generic;
using System.Linq;

namespace GeekDad_Heroes
{
   class Program
   {
     static void Main()
     {
       var Hulk = new Hero { Name = "Hulk", Strength = 99, Speed = 20, Agility = 5, Defense = 99, Stamina = 95 };
         var Thor = new Hero { Name = "Thor", Strength = 85, Speed = 40, Agility = 40, Defense = 85, Stamina = 85 };
         
         List fighters = new List();
         fighters.Add(new Fighter { Hero = Hulk, Power = 20, AttackCount = Hulk.Speed });
         fighters.Add(new Fighter { Hero = Thor, Power = 20, AttackCount = Thor.Speed });

         int round = 0;
         Random diceRoll = new Random();

         //keep doing this until someone is defeated
         while (fighters.All(f => f.Power > 0))
         {
            //let's figure out who's attacking this round, based on Speed
            Fighter attacker = fighters.OrderByDescending(f => f.AttackCount).FirstOrDefault();
            Fighter defender = fighters.OrderByDescending(f => f.AttackCount).Last();

            //up the defender's attack count
            defender.AttackCount += defender.Hero.Speed;

            //how Powerful is the attack (Strength of attacker)?
            var attackerStrength = diceRoll.Next(attacker.Hero.Strength - round, attacker.Hero.Strength);

            //how likely is the attack to land (Agility of defender)?
            var dodge = diceRoll.Next(0, attacker.Hero.Speed) < diceRoll.Next(0, defender.Hero.Agility);

            //how damaging is the attack (Defense of defender)?
            var defenderDefense = diceRoll.Next(defender.Hero.Defense - round, defender.Hero.Defense);

            //did we dodge? if not, calculate damage
            if (!dodge)
            {
               var damage = attackerStrength > defenderDefense ? attackerStrength - defenderDefense : 0;
               defender.Power -= damage;
               Console.WriteLine("{0} attacks {1}! {2} damage done.", attacker.Hero.Name, defender.Hero.Name, damage);
            }
            else
            {
               Console.WriteLine("{0} Misses!", attacker.Hero.Name);
            }

            //what's the score?
            Console.WriteLine("{0}({1}) | {2}({3})", fighters[0].Hero.Name, fighters[0].Power.ToString("0.0"), fighters[1].Hero.Name, fighters[1].Power.ToString("0.0"));
            Console.WriteLine();
            
            //on to the next round...unless someone is too tired, that is
            round++;
            if (round > fighters.Min(f => f.Hero.Stamina))
            {
               break;
            }
         }

         //the fight is over
         if (round > fighters[0].Hero.Stamina)
         {
            throw new Exception(String.Format("{0} is too tired to continue. {1} wins!", fighters[0].Hero.Name, fighters[1].Hero.Name));
         }
         else if (round > fighters[1].Hero.Stamina)
         {
            throw new Exception(String.Format("{0} is too tired to continue. {1} wins!", fighters[1].Hero.Name, fighters[0].Hero.Name));
         }
         else
         {
            Console.WriteLine("The fight is over");
            Console.WriteLine("The winner is: {0}", fighters.OrderByDescending(f => f.Power).FirstOrDefault().Hero.Name);
         }

         //this is just to stop the screen from disappearing in debug mode
         Console.ReadLine();
      }
   }
   
   class Fighter
   {
      internal Hero Hero { get; set; }
      internal double Power { get; set; }
      internal int AttackCount { get; set; }
   }

   class Hero
   {
      internal string Name { get; set; }
      internal int Strength { get; set; }
      internal int Speed { get; set; }
      internal int Agility { get; set; }
      internal int Defense { get; set; }
      internal int Stamina { get; set; }
   }
}

Get the Official GeekDad Books!