Android Games

Android Game Development – Design In-game Entities – The Strategy Pattern

In this part I will try to explain what I understand on good game design elements. I will use droids in the examples and I will script a basic fight simulator to see how they behave.

The problem:

I command a single robot and I want to obliterate my enemies. To face the same type of enemy all over again is boring. I need new challenges and this means new types of enemies. For example in the first level I want only to practice my target. So I need a pretty dumb enemy that does not do much but takes the shots. After I mastered that skill (shooting a helpless droid), I need a bit of a challenge and I want the enemy droid to fight back, but because I am still a beginner I don’t want to die quickly so I need weak droids. After I am over with them I want a tougher challenge. I need better and stronger droids. Not just stronger, but different in behaviour as well as it can get boring killing the same type of enemy over and over again.

The obvious solution:

Create 3 classes for the 3 types of enemy droids. To keep it simple, each droid has 2 abilities: move and attack. It makes sense to create a Droid interface with these two methods and have each droid implement them.

They can move and shoot. Well, not all of them but we can provide an empty implementation for the ones that do nothing.

The droid types:

  • Decoy Droid – will have no weapon and can’t move.
  • Scout Droid – will have a weak weapon and moves fast.
  • Assault Droid – will have a heavy weapon and moves slowly.

Looking at the 3 types we can implement the following simple class diagram:

The interface has 3 simple methods which the droids need to implement:

public interface Droid {

	// display some info of the droid
	public void display();

	// move the droid
	public void move(int x, int y);

	// attack position
	public void shoot(int x, int y);
}

The 3 classes are as follow:

DecoyDroid.java

public class DecoyDroid implements Droid {

	@Override
	public void display() {
		System.out.println("I am a DECOY droid");
	}
		@Override
	public void move(int x, int y) {
		System.out.println("Can't move.");
	}

	@Override
	public void shoot(int x, int y) {
		System.out.println("Have no weapon.");
	}
}

ScoutDroid.java

public class ScoutDroid implements Droid {

	private float damage = 0.5f;

	@Override
	public void display() {
		System.out.println("I am a scout droid");
	}

	@Override
	public void move(int x, int y) {
		System.out.println("Moving QUICKLY to: " + x + "," + y + ".");
	}

	@Override
	public void shoot(int x, int y) {
		System.out.println("Light Laser Canon targeting: " + x + "," + y
				+ ". Damage: " + damage);
	}
}

AssaultDroid.java

public class AssaultDroid implements Droid {

	private float 	damage = 2.5f;
	private boolean loaded = true;

	@Override
	public void display() {
		System.out.println("I am an ASSAULT droid");
	}

	@Override
	public void move(int x, int y) {
		System.out.println("Moving SLOWLY to: " + x + "," + y + ".");
	}

	@Override
	public void shoot(int x, int y) {
		if (loaded) {
			System.out.println("Heavy laser targeting: " + x + "," + y
					+ ". Damage: " + damage);
			loaded = false;
		} else {
			System.out.println("Reloading...");
			loaded = true;
		}
	}
}

Both ScoutDroid and AssaultDroid have the argument damage. This holds the value of the damage inflicted by them.

To give the AssaultDroid a heavy weapon with a slow reload time we added the loaded variable. This way it takes the assault droid two turns to fire its weapon once.

I have created a simple simulator for the droids to take turns to move and shoot.
Run the simulator for this design:

BadDroidSimulator.java

public class BadDroidSimulator {

	public static void main(String[] args) {
		// for generating random numbers
		Random rand = new Random();

		Droid scout = new ScoutDroid();
		Droid assailant = new AssaultDroid();
		Droid decoy = new DecoyDroid();

		scout.display();
		assailant.display();
		decoy.display();

		// shoot-out - each droid fires once per turn
		for (int i = 1; i <= 5; i++) {
			System.out.println("\n<=== BEGIN TURN " + i + " ===>");
			scout.shoot(rand.nextInt(10), rand.nextInt(10));	// we assume this is an enemy position
			scout.move(rand.nextInt(10), rand.nextInt(10));
			System.out.println();
			assailant.shoot(rand.nextInt(10), rand.nextInt(10));
			assailant.move(rand.nextInt(10), rand.nextInt(10));
			System.out.println();
			decoy.shoot(rand.nextInt(10), rand.nextInt(10));
			decoy.move(rand.nextInt(10), rand.nextInt(10));
			System.out.println("<=== END TURN " + i + " ===>");
		}
	}
}

The result (console output) will look like this:

I am a scout droid
I am an ASSAULT droid
I am a DECOY droid
<=== BEGIN TURN 1 ===>
Light Laser Canon targeting: 9,0. Damage: 0.5
Moving QUICKLY to: 4,6.

Heavy laser targeting: 6,2. Damage: 2.5
Moving SLOWLY to: 9,1.

Have no weapon.
Can’t move.
<=== END TURN 1 ===>

<=== BEGIN TURN 2 ===>
Light Laser Canon targeting: 3,4. Damage: 0.5
Moving QUICKLY to: 6,5.

Reloading…
Moving SLOWLY to: 1,6.

Have no weapon.
Can’t move.
<=== END TURN 2 ===>

<=== BEGIN TURN 3 ===>
Light Laser Canon targeting: 6,7. Damage: 0.5
Moving QUICKLY to: 9,7.

Heavy laser targeting: 7,1. Damage: 2.5
Moving SLOWLY to: 2,0.

Have no weapon.
Can’t move.
<=== END TURN 3 ===>

<=== BEGIN TURN 4 ===>
Light Laser Canon targeting: 3,7. Damage: 0.5
Moving QUICKLY to: 1,4.

Reloading…
Moving SLOWLY to: 5,9.

Have no weapon.
Can’t move.
<=== END TURN 4 ===>

<=== BEGIN TURN 5 ===>
Light Laser Canon targeting: 0,8. Damage: 0.5
Moving QUICKLY to: 3,9.

Heavy laser targeting: 1,2. Damage: 2.5
Moving SLOWLY to: 3,2.

Have no weapon.
Can’t move.
<=== END TURN 5 ===>

Challenges to extend the design

The droids take turns to move and shoot. This is all good, but:

  • What if you want to create a hybrid droid? A droid that moves as fast as the scout but with a heavy weapon? You will have to create a new class and copy paste the respective methods from the Scout and Assault droid, right?
  • Also imagine that the shooting mechanism is not that simple and it needs collision detection and so on. For each droid the same redundant code needs to be rewritten.
  • What if the fire power could be enhanced with power ups?
  • What if the droid gains self-conscientiousness and finds a weapon to use it instead of the current one?

I am sure you have plenty ideas on how to enhance the gameplay and extend the world but the most obvious solution (described above) seems ill-suited for this. It requires new droid classes to be created and each droid type will implement its methods separately. Many of these methods are identical. The current design doesn’t allow you to change the droid’s internals at runtime without significant effort.

Here is one proposed solution: Composition and the Strategy Pattern.

Designing a Droid (properly)

A very simple droid consists of a weapon put on a chassis. The first design consisted of a “is a” type relationship. A ScoutDroid is a generic Droid with some peculiarities.

Composition is based on “has a” relationships. A Droid has a Chassis. A Droid has a Weapon. What type of components a droid has, determines its type.

Let’s decompose the Scout Droid for example.
The Scout Droid is a Droid which has a Light Laser Canon has a set of Wheels to move. We want to make the scout move quickly with a light weapon.
The Assault Droid on the other hand is a Droid too but it has a Heavy Laser Canon and it runs on Tracks. This makes it extremely powerful but a bit slow.

Think from a factory perspective. How does a car production line work? You get the chassis with a specific place for the engine, wheels, drive-shaft, gear-box and so on.
All these components are produced separately. The teams that produce them have no idea of the other parts. They must fulfil one criteria: the gearbox must fit in perfectly in its place and connected up with the engine.. Different makes have different ways of doing this. The connector in this instance is the interface.
The engine has a similar story. If it hooks up nicely with the wheels and gearbox then it can be fitted. Its internal design, capacity, power, fuel consumption can be completely different. The engine is one of the car’s components.

So is our droid. But to keep it simple we have only 2 components. We need one generic droid that has all the wirings built so its components will be triggered by the droid through those interfaces. For example a droid needs to only pull the trigger of the weapon and doesn’t care what type of weapon it is as long as it has a trigger. The droid needs to understand the pullTrigger method and to do this it needs to be implemented, in order for us to give weapons to the droid to use.
The same thing with the changing of location. It needs to trigger the movement. The wheels or track or anti-gravity propulsion will take the droid there. The droid only needs to set the coordinates.

To fulfil this we need a class with implemented methods instead of an interface.
We create the abstract Droid class. We make it abstract because we actually implement the methods that trigger the weapon, and action the moving mechanism but we don’t have concrete weapons and movement mechanisms attached to the droid. The assembly of a concrete droid will be delegated to the type constructor along with the description method.

public abstract class Droid {

	protected Weapon 	weapon;		// the weapon which will be used in fights
	protected Chassis	chassis;	// the chassis on which the droid is placed

	public void moveToPosition(int x, int y) {
		System.out.print(id + " > " );
		chassis.moveTo(x, y);
	}

	/**
	 *  Engages the position on the screen whether it is occupied by
	 *  an enemy or not. Each strategy should decide how to do it.
	 */
	public void attackPosition(int x, int y) {
		System.out.print(id + " > ");
		weapon.useWeapon(new Vector2f(x, y));
	}

	/**
	 * Displays some info on the droid
	 */
	public abstract void display();
}

If you examine the class you will see that the Droid has 3 methods from which 2 are implemented. It also has two components: Weapon and Chassis.
The components are interfaces so the droid does not know what it is doing when triggering the actions on them. It is all delegated to the implementation.

The interfaces are as follows:

Weapon.java

public interface Weapon {
	/**
	 * The trigger to use the weapon.
	 * @param target - where on the map is the target
	 */
	public void useWeapon(Vector2f target);

	/**
	 * Returns the description of the weapon
	 */
	public String getDescription();
}

Chassis.java

public interface Chassis {
	/**
	 * Delegates the movement to the supporting chassis and
	 * tries to move the unit to x,y
	 */
	public void moveTo(int x, int y);

	/**
	 * Returns the description of the chassis
	 */
	public String getDescription();
}

We will enhance our base Droid class. We will add setter and getter methods for both Weapon and Chassis. This will allow us to change the droid’s behaviour at runtime. This is what the strategy pattern is all about. A Droid has behaviours: it can use a weapon and it can move. These two strategies (behaviours) need to be implemented.

We also add an id which will be unique in our game for each droid instance. I use a very simple id generation strategy. I increment the nextId static field and append it to the concrete droid type prefix in the constructor for each type.

Here is the new Droid class:

public abstract class Droid {

	protected static int nextId	= 0;	// the next available ID

	protected String 	id;			// unique id
	protected Weapon 	weapon;		// the weapon which will be used in fights
	protected Chassis	chassis;	// the chassis on which the droid is placed

	// the unique ID of the droid in the game
	public String getId() {
		return id;
	}

	public Weapon getWeapon() {
		return weapon;
	}
	public void setWeapon(Weapon weapon) {
		this.weapon = weapon;
	}

	public Chassis getChassis() {
		return chassis;
	}
	public void setChassis(Chassis chassis) {
		this.chassis = chassis;
	}

	public void moveToPosition(int x, int y) {
		System.out.print(id + " > " );
		chassis.moveTo(x, y);
	}

	/**
	 *  Engages the position on the screen whether it is occupied by
	 *  an enemy or not. Each strategy should decide how to do it.
	 */
	public void attackPosition(int x, int y) {
		System.out.print(id + " > ");
		weapon.useWeapon(new Vector2f(x, y));
	}

	/**
	 * Displays some info on the droid
	 */
	public abstract void display();
}

Let’s build some weapons

NoWeapon.java

/**
 * This is a null object. A null object is a dummy that does nothing and it
 * is a mere place-holder and eliminates the need to check for null.
 * @author impaler
 *
 */
public class NoWeapon implements Weapon {

	@Override
	public void useWeapon(Vector2f target) {
		// We are doing nothing
		System.out.println("No weapon equipped!");
	}

	@Override
	public String getDescription() {
		return "Nothing";
	}
}

This is the null object. The class description should give you an idea what it is.

LightLaserCanon.java

/**
 * This is a light laser cannon whit a quick reload time and high accuracy
 *
 * @author impaler
 *
 */
public class LightLaserCanon implements Weapon {

	private float damage = 0.5f; // the damage inflicted

	@Override
	public void useWeapon(Vector2f target) {
		System.out.println("Shooting my laser canon to " + (int)target.getX() + ","
				+ (int)target.getY() + ". Bang on! Done " + damage + " damage.");
	}

	@Override
	public String getDescription() {
		return "First generation laser canon. Street use only!";
	}
}

HeavyLaserCanon.java

/**
 * This is a heavy assault laser cannon with high accuracy but slow reload time.
 * @author impaler
 */
public class HeavyLaserCanon implements Weapon {

	private boolean loaded 	= true;	// after fire needs to be reloaded
	private float 	damage 	= 1.5f;	// the damage is considerable

	@Override
	public void useWeapon(Vector2f target) {
		if (loaded) {
			// we fire the canon
			System.out.println("Eat this! Laser beam hit target (" + (int)target.getX() + "," + (int)target.getY() + ") and dealt " + damage + " damage.");
			// next time needs reloading
			loaded = false;
		} else {
			System.out.println("Darn! Out of ammo! Reloading...");
			loaded = true;
		}
	}

	@Override
	public String getDescription() {
		return "DASS-5000 - The ultimate in siege weaponry provided by Obviam Enterprises.";
	}
}

You might notice the Vector2f class. This is a very basic 2D vector class which currently holds the x and y coordinates. Nothing more. You can find it in the downloaded source.

Let’s build some chassis

The getDescription() method says what the chassis is like.

NoChassis.java – null object (see weapons)

public class NoChassis implements Chassis {

	@Override
	public void moveTo(int x, int y) {
		System.out.println("It's just a frame. Can't move.");
	}

	@Override
	public String getDescription() {
		return "It's just a frame.";
	}
}

SteelStand.java

public class SteelStand implements Chassis {

	@Override
	public void moveTo(int x, int y) {
		System.out.println("Can't move.");
	}

	@Override
	public String getDescription() {
		return "Unmovable reinforced steel stand ideal for turrets and defensive units.";
	}
}

Wheels.java

public class Wheels implements Chassis {

	@Override
	public void moveTo(int x, int y) {
		System.out.println("Speeding to " + x + "," + y + " on my wheels!");
	}

	@Override
	public String getDescription() {
		return "4 wheel drive, very fast on flat terrain but struggling through obstacles.";
	}
}

Track.java

public class Track implements Chassis {

	@Override
	public void moveTo(int x, int y) {
		System.out.println("Don't get in my way! Moving slowly to: " + x + "," + y + ".");
	}

	@Override
	public String getDescription() {
		return "Slow moving tracks but able to go through many obstacles.";
	}
}

Now we can assemble our droids

First let’s create a DecoyDroid. This droid will have no weapon and will be placed on a steel stand. It’s for our target practice, remember?

DecoyDroid.java

public class DecoyDroid extends Droid {

	public DecoyDroid() {
		id = "DCY-" + (++Droid.nextId);
		weapon = new NoWeapon();
		chassis = new SteelStand();
	}

	@Override
	public void display() {
		System.out.println("+--------------------------------------------------------------------------------------------+");
		System.out.println("| I am a DECOY droid.");
		System.out.println("|\tID: " + id);
		System.out.println("|\tWeapon: " + weapon.getDescription());
		System.out.println("|\tChassis: " + chassis.getDescription());
		System.out.println("+--------------------------------------------------------------------------------------------+");
	}
}

Examine the default constructor. It creates an id and assigns an instance of NoWeapon and SteelStand to the droid.
The display() method is more elaborate than before but just to describe the droid better. It makes use of the components’ descriptions too.
If you instantiate a DecoyDroid and call its display method you will get a nice description of it.

+——————————————————————————————–+
| I am a DECOY droid.
|	ID: DCY-3
|	Weapon: Nothing
|	Chassis: Unmovable reinforced steel stand ideal for turrets and defensive units.
+——————————————————————————————–+

Let’s build the rest of the types:

ScoutDroid.java

public class ScoutDroid extends Droid {

	public ScoutDroid() {
		id = "SCT-" + (++Droid.nextId);
		weapon = new LightLaserCanon();
		chassis = new Wheels();
	}

	@Override
	public void display() {
		System.out.println("+--------------------------------------------------------------------------------------------+");
		System.out.println("| I am a SCOUT droid.");
		System.out.println("|\tID: " + id);
		System.out.println("|\tWeapon: " + weapon.getDescription());
		System.out.println("|\tChassis: " + chassis.getDescription());
		System.out.println("+--------------------------------------------------------------------------------------------+");
	}
}

AssaultDroid.java

public class AssaultDroid extends Droid {

	public AssaultDroid() {
		id = "ASS-" + (++Droid.nextId);
		weapon = new HeavyLaserCanon();
		chassis = new Track();
	}

	@Override
	public void display() {
		System.out.println("+--------------------------------------------------------------------------------------------+");
		System.out.println("| I am an ASSAULT droid.");
		System.out.println("|\tID: " + id);
		System.out.println("|\tWeapon: " + weapon.getDescription());
		System.out.println("|\tChassis: " + chassis.getDescription());
		System.out.println("+--------------------------------------------------------------------------------------------+");
	}
}

You will notice that the only things needed to be implemented are the constructor – which adds the chassis and weapon – and the display() method.
The following diagram shows the new architecture:

Let’s create a test script for it. We’ll simulate 5 turns in which each droid will use its weapon and move to a random location. Check the behaviour of each weapon and you will notice that the heavy laser will fire once every 2 turns. To make it interesting, in turn 4 we give a HeavyLaserCanon to DecoyDroid. Look at how it changes the droid’s behaviour and it starts firing. This is a hybrid droid created on the fly at runtime.

The simulator code (DroidSimulator.java):

public class DroidSimulator {

	public static void main(String[] args) {
		// for generating random numbers
		Random rand = new Random();

		Droid scout = new ScoutDroid();
		Droid assailant = new AssaultDroid();
		Droid decoy = new DecoyDroid();

		scout.display();
		assailant.display();
		decoy.display();

		// shoot-out - each droid fires once per turn
		for (int i = 1; i <= 5; i++) {
			System.out.println("\n<=== BEGIN TURN " + i + " ===>");
			// in turn 3 decoy droid is given an assault canon
			if (i == 4) {
				decoy.setWeapon(new HeavyLaserCanon());
				System.out.println("* " + decoy.getId() + " acquired " + decoy.getWeapon().getDescription() + "\n");
			}
			scout.attackPosition(rand.nextInt(10), rand.nextInt(10));	// we assume this is an enemy position
			scout.moveToPosition(rand.nextInt(10), rand.nextInt(10));
			System.out.println();
			assailant.attackPosition(rand.nextInt(10), rand.nextInt(10));
			assailant.moveToPosition(rand.nextInt(10), rand.nextInt(10));
			System.out.println();
			decoy.attackPosition(rand.nextInt(10), rand.nextInt(10));
			decoy.moveToPosition(rand.nextInt(10), rand.nextInt(10));
			System.out.println("<=== END TURN " + i + " ===>");
		}
	}
}

The output:

+——————————————————————————————–+
| I am a SCOUT droid.
|	ID: SCT-1
|	Weapon: First generation laser canon. Street use only!
|	Chassis: 4 wheel drive, very fast on flat terrain but struggling through obstacles.
+——————————————————————————————–+
+——————————————————————————————–+
| I am an ASSAULT droid.
|	ID: ASS-2
|	Weapon: DASS-5000 – The ultimate in siege weaponry provided by Obviam Enterprises.
|	Chassis: Slow moving tracks but able to go through many obstacles.
+——————————————————————————————–+
+——————————————————————————————–+
| I am a DECOY droid.
|	ID: DCY-3
|	Weapon: Nothing
|	Chassis: Unmovable reinforced steel stand ideal for turrets and defensive units.
+——————————————————————————————–+
<=== BEGIN TURN 1 ===>
SCT-1 > Shooting my laser canon to 0,3. Bang on! Done 0.5 damage.
SCT-1 > Speeding to 0,2 on my wheels!

ASS-2 > Eat this! Laser beam hit target (3,4) and dealt 1.5 damage.
ASS-2 > Don’t get in my way! Moving slowly to: 3,8.

DCY-3 > No weapon equipped!
DCY-3 > Can’t move.
<=== END TURN 1 ===>

<=== BEGIN TURN 2 ===>
SCT-1 > Shooting my laser canon to 4,0. Bang on! Done 0.5 damage.
SCT-1 > Speeding to 5,0 on my wheels!

ASS-2 > Darn! Out of ammo! Reloading…
ASS-2 > Don’t get in my way! Moving slowly to: 1,6.

DCY-3 > No weapon equipped!
DCY-3 > Can’t move.
<=== END TURN 2 ===>

<=== BEGIN TURN 3 ===>
SCT-1 > Shooting my laser canon to 3,0. Bang on! Done 0.5 damage.
SCT-1 > Speeding to 0,6 on my wheels!

ASS-2 > Eat this! Laser beam hit target (9,1) and dealt 1.5 damage.
ASS-2 > Don’t get in my way! Moving slowly to: 8,0.

DCY-3 > No weapon equipped!
DCY-3 > Can’t move.
<=== END TURN 3 ===>

<=== BEGIN TURN 4 ===>
* DCY-3 acquired DASS-5000 – The ultimate in siege weaponry provided by Obviam Enterprises.

SCT-1 > Shooting my laser canon to 8,6. Bang on! Done 0.5 damage.
SCT-1 > Speeding to 2,3 on my wheels!

ASS-2 > Darn! Out of ammo! Reloading…
ASS-2 > Don’t get in my way! Moving slowly to: 0,6.

DCY-3 > Eat this! Laser beam hit target (9,4) and dealt 1.5 damage.
DCY-3 > Can’t move.
<=== END TURN 4 ===>

<=== BEGIN TURN 5 ===>
SCT-1 > Shooting my laser canon to 1,7. Bang on! Done 0.5 damage.
SCT-1 > Speeding to 1,9 on my wheels!

ASS-2 > Eat this! Laser beam hit target (1,4) and dealt 1.5 damage.
ASS-2 > Don’t get in my way! Moving slowly to: 3,6.

DCY-3 > Darn! Out of ammo! Reloading…
DCY-3 > Can’t move.
<=== END TURN 5 ===>

Note in turn 4 how DecoyDroid got the new weapon and changed its behaviour (the yellow line). Now you should understand the null object pattern as well.

As it stands now, it is easy to create new weapons, chassis and droids by keeping the code to a minimum. Always favour composition over inheritance in these situations. You can create a very elaborate droid, with shields, sensors, an array of weapons and also an AI component which decides what weapons to use according to the situation. If you have noticed, I used the term “use” instead of fire, because a weapon can be a melee one too and not necessarily a ranged weapon.

The downloadable project contains only the strategy pattern implementation. The simple inheritance approach is left out. Play with it and get the grips of this useful pattern as I will use it extensively and it is considered good design. It is a simple Java application, it is not Android enabled.

Dowload the source here (obviam.compositions.part1.tgz).

In the following parts I’ll try to add some AI capabilities and how to compose the final droid from images of its components.

Reference: Design In-game Entities. Object Composition Strategies. Part 1 – The Strategy Pattern from our JCG partner Tamas Jano from “Against The Grain” blog.

Do not forget to check out our new Android Game ArkDroid (screenshots below). You feedback will be more than helpful!
Related Articles:
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

3 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
jmartrican
jmartrican
12 years ago

I am also working on the same thing, but not for Android.  I did the same thing you did at first but noticed that I was creating too many EnemyUnit children types.  What I realized is that what separates one EnemyUnit child type from another can be packaged nicely into an EnemyUnitBuilder configuration class.  So now my EnemyUnit class takes an EnemyUnitBuilder object as a param for its constructor…. it will then construct itself using the fields in the EnemyUnitBuilder. Furthermore I used the same techniques for other object types in the game, like Map and Round.  The factories keep the configuration values… Read more »

Philipp
Philipp
10 years ago

Great guide, very helpful! I just have a question: Why do we implement the Chassis and Weapon interfaces, but extend the Droid class? Where is the difference? Couldn’t we also just create the Droid class as an interface and implement it like the other ones?

Rafael
Rafael
10 years ago
Reply to  Philipp

I think it’s just a matter of re-using code.
In the Droid class, we are implementing several useful methods like get/setChassis, get/setWeapon, moveToPosition and attackPosition that are used in all of its subclasses.
Since interfaces can’t implement methods, if Droid was an interface, then all subclasses (DecoyDroid, ScoutDroid and AssaultDroid) would need to implement those methods by repeating the code.

Back to top button