Amazon Interview Question
SDE1sCountry: India
Interview Type: In-Person
At first glance, this question appeals for inheritance but before using inheritance, it is important to understand what deviations are possible from base functionality. The question doesn't clearly speak about that. If properties of class will be same, there is no need for inheritance.
Different weapons have different predefined damage. We can define Weapon class with following properties -
public class Weapon{
String name;
int damagingEffect;
int bulletsLeft;
public Weapon(name, damagingEffect, bulletsLeft) {// initialize all}
// returns damaging effect
public int fireWeapon() {
if (bulletsLeft == 0) {
throw new OutOfAmmoException(name);
}
bulletsLeft--;
return damagingEffect;
}
public void reload(int newBullets) {
bullets+= newBullets;
}
}
As the game initializes, different instances of this class can represent different weapons.
Example
Weapon gun = new Weapon("gun", 3, 100);
Weapon rocketLauncher = new Weapon("Rocket Launcher", 50, 20);
Weapon missile = new Weapon("Missile", 100, 5);
Hero is initialized with Set of Weapons. He can use one as current.
public class Hero {
Map<String, Weapon> weapons = new HashMap<String, Weapon>();
Weapon currentWeapon;
public Hero(Set<Weapon> weapons) {
for(Weapon weapon:weapons)
this.weapons.put(weapon.name(), weapon);
currentWeapon = weapons.get(0);
}
public void setCurrentWeapon(String name) {
if (!weapons.contains(name)) {
throw new NoSuchWeaponException(name);
}
currentWeapon = weapons.get(name);
}
// returns damaging effect
public int fire() {
return currentWeapon.fireWeapon();
}
public boolean isUnArmed() {
// check and return true if all weapons are out of bullets
}
}
Monster can captured in a same class.
public class Monster {
String name;
int health;
public Monster(String name, int health) {// initialize}
// returns true if dead
public boolean damageHealth(int damageEffect) {
health-=damageEffect;
return isDead();
}
public boolean isDead() {
return health<=0;
}
}
Example monsters -
Monster yeti = new Monster("Yeti", 20);
Monster dino = new Monster("Dinosaur", 40);
Then you initialize the Game, where all action is happening. Each level will have different instance of game class.
public class Game {
Hero hero;
int level;
Map<String, Monster> monsters;
enum LevelResult {WON, LOST, PLAYING};
public Game(Hero hero, Set<Monster> monsters, int level) {//initialize}
public void doEncounter(String monsterName) {
Monster monster = monsters.get(monsterName);
boolean isDead = monster.damageHealth(hero.fire());
if (monster.isDead()) {
monsters.remove(monsterName);
}
}
public void misFire() {
hero.fire(); // didn't hit any monster
}
public LevelResult getLevelResult() {
if (mosters.isEmpty()) {
return LevelResult.WON;
} else if (hero.isUnArmed()) {
return LevelResult.LOST;
} else {
return LevelResult.PLAYiNG;
}
}
}
Instead of doing that you could (and I think you should) do
//Maybe need an abstract class for hero, but ..., ignore it
public class Heros
{
public Weapon weapon;
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
public void ShotMonster(Monster ms)
{
for (int i = 0; i < 11; i++)
{
ms.Hit(this.wp.Shot()); // I dont know if it's the correct syntax
}
}
}
public class TetGame
{
public static void TestGame1()
{
Weapon wp1 = new Weapon1("Gun", 5, 100, 10);
Monster ms1 = new Monster1("Ghost", 300, 5);
Heros ho1 = new Heros();
ho1.ShotMonster(wp1,ms1);
}
}
You could also make a MonsterFactory and a WeaponFactory to have more flexibility on how you create your Monsters/Weapons.
game code:
class Game
{
private List<Monster> _monster;
private List<Weapon> _weapon;
public void initGame()
{
_monster = new List<Monster>(){
MonCreator.getInstance().CreateMonster("Normal ghost", new NormalLevel()),
MonCreator.getInstance().CreateMonster("Master ghost", new MasterLevel()),
MonCreator.getInstance().CreateMonster("Normal tiger", new NormalLevel()),
MonCreator.getInstance().CreateMonster("Master tiger", new MasterLevel()),};
_weapon = new List<Weapon>(){
WeapCreator.getInstance().CreateWeap("China arraw"),
WeapCreator.getInstance().CreateWeap("China arraw"),
WeapCreator.getInstance().CreateWeap("China arraw"),
WeapCreator.getInstance().CreateWeap("AK47 gun"),
WeapCreator.getInstance().CreateWeap("AK47 gun")};
}
public void play()
{
foreach (Weapon w in _weapon)
{
foreach (Monster m in _monster)
w.Shoot(m);
}
foreach (Monster m in _monster)
m.GetStatus();
}
}
class MonCreator
{
static private MonCreator _creator;
static public MonCreator getInstance()
{
if (null == _creator)
_creator = new MonCreator();
return _creator;
}
public Monster CreateMonster(string name, Level lv)
{
if (name.IndexOf("ghost") >= 0)
return new Monster(name, 50, 50, lv);
if (name.IndexOf("tiger") >= 0)
return new Monster(name, 30, 80, lv);
return new Monster(name, 30, 30, lv);
}
}
class WeapCreator
{
static private WeapCreator _creator;
static public WeapCreator getInstance()
{
if (null == _creator)
_creator = new WeapCreator();
return _creator;
}
public Weapon CreateWeap(string name)
{
if (name.IndexOf("arrow") >= 0)
return new Weapon(name, 0.6, 20);
if (name.IndexOf("gun") >= 0)
return new Weapon(name, 0.9, 100);
return new Weapon(name, 0.3, 20);
}
}
class Monster
{
private int _life;
private double _speed;
private Level _level;
private string _Name;
public double Speed() { return _speed * _level.Move(); }
public void Hit(int dam) { _life -= (int)(dam*_level.Defence()); }
public Monster(string name, int life, double speed, Level lv) { _Name = name; _life = life; _speed = speed; _level = lv; }
public void GetStatus() {
if(_life <=0)
System.Console.WriteLine("{0} is dead", _Name);
else
System.Console.WriteLine("{0} life is :{1}, and speed is: {2}",_Name,_life,_speed);
}
}
class Weapon
{
private double _acurate;
private int _damage;
private string _Name;
public Weapon(string name, double acurate, int damage) { _acurate = acurate; _Name = name; _damage = damage; }
public void Shoot(Monster monster)
{
Random r = new Random();
int temp = r.Next(100);
if(temp*_acurate > monster.Speed())
monster.Hit(_damage);
}
}
class Level
{
public virtual double Move() { return 1.0; }
public virtual double Defence() { return 1.0; }
}
class NormalLevel : Level
{
public override double Move() { return 1.0; }
public override double Defence() { return 1.0; }
}
class MasterLevel : Level
{
public override double Move() { return 1.5; }
public override double Defence() { return 0.7; }
}
clint code:
{
Game _game = new Game();
_game.initGame();
_game.play();
}
We’ll use Flyweight design pattern for this game to create Monsters or Weapons. It’ll help us a lot because weapon or monster are not going to change their appearance or properties, all that can happen to them is extrinsic properties.
We’ll have a Monster Base class. Now Ghost, Demon, Sorceror etc. ae the concrete classes derived from this class. Same Design will be followed for Weapon class also. Each weapon class will also define the damage it’ll cause to monster.
We’ll have a Player class.
We’ll have a Game class which maintains the resource pool for the already created object. Any new request will be satisfied from this pool. Say once a monster is created and returned, we need t devise a mechanism where any loss to this monster is visible to this monster only and not to any other sharing monster. So we’ll have to use some extrinsic functions that don’t operate on this object but take help of this object. Say we use a wrapper object (Decorator Design Pattern) which contains this object and health information. When health is 0, the wrapper object dies and forgets the shared monster object but doesn’t delete it. I think shared pointers will come handy here. We’ll save a lot of cost by sharing mosnters and weapons among thousand of monster and weapon objects.
Now comes the point that monster and weapon properties will change based upon level. I think that we can assign this extra responsibility to the Wrapper classes. These wrapper classes can be created using Decorator Design Pattern. Game Class will work only with the Decorator object. So once a monster or weapon is requested, Monster and Weapon classes return already created object from resource pool but Decorator behaves differently (Based upon the Level) and attach different properties (health, attack etc.) to these objects.
I am not sure what's the best solution for this. The below is my example.
Output:
- Jasonhuangx March 11, 2014I am still alive [202], be careful!!
I am still alive [104], be careful!!
I am still alive [6], be careful!!
oops, die!
oops, run out of bullets!