GAZAR

Principal Engineer | Mentor

Pokemon Game Algorithm

Pokemon Game Algorithm

What is this challenge for?

We want to understand the way you think about problems, and how you write code to tackle them. We’re not looking for the most efficient algorithm or the purest design, we’re looking for the simplest solution to the problems in front of you.

We’re not going to give much in the way of guidance as to the specifics of implementation. If, for example, if you think a class needs an attribute or a method, you go ahead and do it. You’re in charge.

What do you need to prepare?

You’ll share your screen with Google Meet and code in any IDE of your choice. Please test your mic, camera and screen-sharing set up before the call. If you have chosen to code live, we recommend that you have a working environment ready (for example Typescript and Jest running)

How should solutions be presented?

Ideally, solutions will be written in a typed language with some basic tests. That being said, you should use the language with which you feel most fluent. We’ve seen pretty much every language so far so don’t be afraid to use what you know!

So, what’s the challenge?

Imagine you're a collector and all around you there's a world with Creatures that you can catch and collect.

Your task is to write a program that allows a Collector (like you) to interact with these Creatures.

The challenge is broken down into four parts.

Part 1

Define some domain classes to model this application.

There are many different species of Creature, each belonging to a family (e.g. flyer, swimmer, runner) and a species (e.g. Bird, Shark, Lion). Feel free to invent your own test-cases!

Both Collectors and Creatures need to have a Position, so we can know how close different Entities are to each other. You may need some concept of a World or Map to hold references to the Entities within it.

Part 2

Gotta collect ‘em all! In this part we’ll implement the means for a Collector to catch a Creature and add it to their collection.

First, we need to know which Creatures are nearby to a Collector. Write code to calculate this.

How you define “nearby” is completely up to you.

A Collector can catch a random nearby Creature and add them to his/her collection. Write code to allow this.

import type Board from "./Board";
import Creature from "./Creature";

class Collector {
  public name: string = "👤";
  public x: number;
  public y: number;
  private inventory: Creature[] = [];
  private board: Board;

  constructor(board: Board, x: number, y: number) {
    this.x = x;
    this.y = y;
    this.board = board;
    board.getCell(x, y)?.occupy(this);
  }

  public setPosition(x: number, y: number) {
    this.x = x;
    this.y = y;
  }

  public getPosition() {
    return { x: this.x, y: this.y };
  }

  public getName() {
    return this.name;
  }

  public findNearestCreature() {
    let nearestCreature = null;
    let nearestDistance = Infinity;
    for (let i = this.x - 5; i <= this.x + 5; i++) {
      for (let j = this.y - 5; j <= this.y + 5; j++) {
        const cell = this.board.getCell(i, j);
        if (cell && cell.occupied && cell.occupant instanceof Creature) {
          const distance = Math.abs(this.x - i) + Math.abs(this.y - j);
          if (distance < nearestDistance) {
            nearestCreature = cell.occupant;
            nearestDistance = distance;
          }
        }
      }
    }

    return nearestCreature;
  }

  public searchAndCapture() {
    const creature = this.findNearestCreature();
    if (creature) {
      const creatureCell = this.board.getCell(creature.x, creature.y);
      const collectorCell = this.board.getCell(this.x, this.y);
      if (creatureCell) {
        this.inventory.push(creature);
        collectorCell?.unoccupy();
        this.setPosition(creature.x, creature.y);
        creature.capture();
        creatureCell.occupy(this);
      }
      return true;
    }
    return false;
  }

  public canIMove(newPosition: { x: number; y: number }) {
    const cell = this.board.getCell(newPosition.x, newPosition.y);
    return cell?.isFree();
  }

  public move(direction: string) {
    let newPosition = { x: this.x, y: this.y };
    switch (direction) {
      case "up":
        newPosition.y++;
        break;
      case "down":
        newPosition.y--;
        break;
      case "left":
        newPosition.x--;
        break;
      case "right":
        newPosition.x++;
        break;
    }

    if (this.canIMove(newPosition)) {
      this.board.getCell(this.x, this.y)?.unoccupy();
      this.setPosition(newPosition.x, newPosition.y);
      this.board.getCell(newPosition.x, newPosition.y)?.occupy(this);
    }
  }
}
export default Collector;

And for Cell

import type Collector from "./Collector";
import type Creature from "./Creature";

class Cell {
  public x: number;
  public y: number;
  public occupied: boolean = false;
  public occupant: Creature | Collector | null = null;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }

  public occupy(occupant: Creature | Collector) {
    this.occupied = true;
    this.occupant = occupant;
  }

  public unoccupy() {
    this.occupied = false;
    this.occupant = null;
  }

  public isOccupied() {
    return this.occupied;
  }

  public isFree() {
    return !this.occupied;
  }
}

export default Cell;

Then Board

import Cell from "./Cell";
class Board {
  private boardSize = 30;
  private cells: Cell[][] = [];

  constructor() {
    for (let i = 0; i < this.boardSize; i++) {
      this.cells[i] = [];
      for (let j = 0; j < this.boardSize; j++) {
        this.cells[i][j] = new Cell(i, j);
      }
    }
  }

  public getCell(x: number, y: number) {
    if (x < 0 || x >= this.boardSize || y < 0 || y >= this.boardSize) {
      return null;
    }
    return this.cells[x][y];
  }

  public getCells() {
    return this.cells;
  }

  public getBoardSize() {
    return this.boardSize;
  }
}

export default Board;

And Finally you can have the Creature

enum Ability {
  FLY = "fly",
  SWIM = "swim",
  WALK = "walk",
}

class Creature {
  public ability: Ability;
  public x: number;
  public y: number;
  public name: string;
  public captured: boolean = false;

  constructor(name: string, x: number, y: number, ability: Ability) {
    this.name = name;
    this.ability = ability;
    this.x = x;
    this.y = y;
  }

  setX(x: number) {
    this.x = x;
  }
  setY(y: number) {
    this.y = y;
  }

  getX() {
    return this.x;
  }
  getY() {
    return this.y;
  }
  getName() {
    return this.name;
  }
  getAbility() {
    return this.ability;
  }
  public capture() {
    this.captured = true;
  }
}

class Bird extends Creature {
  constructor(x: number, y: number) {
    super("🐦", x, y, Ability.FLY);
  }
}

class Fish extends Creature {
  constructor(x: number, y: number) {
    super("🐟", x, y, Ability.SWIM);
  }
}

class Dog extends Creature {
  constructor(x: number, y: number) {
    super("🐕", x, y, Ability.WALK);
  }
}

export default Creature;
export { Bird, Fish, Dog };
export { Ability };