# How I solved Bomberman challenge on HackerRank

## Rules

• Bombs will explode in 3 seconds
• When a bomb explodes it clears its tile and 1 tile up, down, left, right
• Bombs that are about to go off explode at the same time
• There are no chain reactions
• If a bomb explodes next to another that is not about to go off then the nearby bomb is just cleared

## Iteration

1. Bomberman plants some bombs (you’re given an input with the map)
2. A second passes
3. A second passes. Bomberman plants bombs on all empty slots
4. A second passes
5. Repeat 3 and 4 until N seconds passes

## Input

• `R` = number of rows
• `C` = number of cols
• `S` = number of seconds
• `O` = bomb
• `.` = empty
`R C SO.....`

## Solving

`export function extractParams(input: string) {  return {    cols: undefined,    matrix: undefined,    rows: undefined,    seconds: undefined,  };}`
`describe('extractParams', () => {  it('returns parameters as object', () => {    const settings = extractParams(sampleInput);    expect(settings.cols).toBe(7);    expect(settings.matrix).toEqual(expectedMatrix);    expect(settings.rows).toBe(6);    expect(settings.seconds).toBe(3);  });});`
`export function extractParams(input: string): Settings {  const pieces = input.split('\n');  const [rows, cols, seconds] = pieces    .split(' ')    .map(setting => +setting);  const matrix = pieces.slice(1)    .map(row => row.split('').map(e => e === 'O' ? 3 : emptySlot));  return {    cols,    matrix,    rows,    seconds, // hint: change *something* here for performance  };}`
`export function* tick({ cols, matrix, rows, seconds }: Settings) {  let result = [    ...matrix.map(row => [...row]),  ];  let currentSeconds = seconds + 1;  while (currentSeconds--) {    for (let row = 0; row < rows; row++) {      for (let col = 0; col < cols; col++) {        if (result[row][col] === 0) {          explode(result, { col, cols, row, rows });        }      }    }    yield result;  }}`
`export function explode(matrix: Array<number[]>, { col, cols, row, rows }) {  matrix[row][col] = emptySlot;}`
• Boundaries: I shouldn’t change a position that doesn’t even exist in the matrix (ex: col -1)
• Bombs explode at the same time: I can’t clear the position of a bomb that is also exploding, because it is exploding “at the same time”, so I need to keep it there until I get to it and make it explode
`export function explode(matrix: Array<number[]>, { col, cols, row, rows }) {  if (col - 1 >= 0) {    matrix[row][col - 1] = nextStateAfterExplosion(matrix[row][col - 1]);  }  if (col + 1 < cols) {    matrix[row][col + 1] = nextStateAfterExplosion(matrix[row][col + 1]);  }  if (row - 1 >= 0) {    matrix[row - 1][col] = nextStateAfterExplosion(matrix[row - 1][col]);  }  if (row + 1 < rows) {    matrix[row + 1][col] = nextStateAfterExplosion(matrix[row + 1][col]);  }  matrix[row][col] = emptySlot;}`
• Bomb could be on
• top left (no neighbors to the left or top)
• middle left
• bottom left
• center
• and so on…
`it('returns map with cleared neighbors after explosion', () => {  const matrix = [    [0, 2],    [3, 1],  ];  const expected = [    [emptySlot, emptySlot],    [emptySlot, 1],  ];  explode(matrix, { col: 0, cols: 2, row: 0, rows: 2 });  expect(matrix).toEqual(expected);});`
`export function nextStateAfterExplosion(element: number) {  return element === 0 ? element : emptySlot;}`
`describe('nextState', () => {  it('returns emptySlot value when seconds to explode is 3', () => {    expect(nextStateAfterExplosion(3)).toBe(emptySlot);  });  it('returns emptySlot value when seconds to explode is 2', () => {    expect(nextStateAfterExplosion(2)).toBe(emptySlot);  });  it('returns emptySlot value when seconds to explode is 1', () => {    expect(nextStateAfterExplosion(1)).toBe(emptySlot);  });  it('returns 0 when seconds to explode is 0', () => {    expect(nextStateAfterExplosion(0)).toBe(0);  });  it('returns emptySlot value when it is an empty slot', () => {    expect(nextStateAfterExplosion(emptySlot)).toBe(emptySlot);  });});`
`export function* tick({ cols, matrix, rows, seconds }: Settings) {  let result = [    ...matrix.map(row => [...row]),  ];  let currentSeconds = seconds + 1;  while (currentSeconds--) {    for (let row = 0; row < rows; row++) {      for (let col = 0; col < cols; col++) {        if (result[row][col] === 0) {          explode(result, { col, cols, row, rows });        }        else {          if (result[row][col] > 0) { // bomb not ready            result[row][col] = nextStateAfterTick(result[row][col]);          }        }      }    }    yield result;  }}`
`export function nextStateAfterTick(element: number) {  if (element === 0) {    return element;  }  if (element === emptySlot) {    return emptySlot;  }  return element - 1;}`
`describe('nextStateAfterTick', () => {  it('returns 2 when seconds to explode is 3', () => {    expect(nextStateAfterTick(3)).toBe(2);  });  it('returns 1 when seconds to explode is 2', () => {    expect(nextStateAfterTick(2)).toBe(1);  });  it('returns 0 when seconds to explode is 1', () => {    expect(nextStateAfterTick(1)).toBe(0);  });  it('returns 0 when seconds to explode is 0', () => {    expect(nextStateAfterTick(0)).toBe(0);  });  it('returns emptySlot value when it is an empty slot', () => {    expect(nextStateAfterTick(emptySlot)).toBe(emptySlot);  });});`
`export function* tick({ cols, matrix, rows, seconds }: Settings) {  let turn = 0;  let result = [    ...matrix.map(row => [...row]),  ];  let currentSeconds = seconds + 1;  while (currentSeconds--) {    for (let row = 0; row < rows; row++) {      for (let col = 0; col < cols; col++) {        if (result[row][col] === 0) {          explode(result, { col, cols, row, rows });        }        else {          if (result[row][col] > 0) {            result[row][col] = nextStateAfterTick(result[row][col]);          }        }      }      yield result;    }    // plant bombs    if ((turn & 1) === 1) {      result = [        ...result.map(row => row.map(col => col === -1 ? 3 : col)),      ];    }    turn++;  }}`
`export function processData(input: string) {  const settings = extractParams(input);  const tickGen = tick(settings);  let fieldState;  // step all the way to the end  // as I said, it doesn't make much sense here  // but it is fun  // we could make a replay of the game with it  for (const state of tickGen) {    fieldState = state;  }  return fieldState;}`

## Debug

`if (result[row][col] === 0) {  explode(result, { col, cols, row, rows });  yield result; // here}else {  if (result[row][col] > 0) {    result[row][col] = nextStateAfterTick(result[row][col]);  }  yield result; // here}`

--

--

--

## More from Bruno Leonardo Michels

Love podcasts or audiobooks? Learn on the go with our new app.

## Material UI in Svelte using Smelte ## How to Implement Push Notifications in PWA using React ## AngularJS vs Angular2+ ## We choose React & TypeScript not because they’re easy, but because they’re hard. ## What is Webpack? How Webpack Quickly Speeds Up Your Website and Generate Revenue for Your Business ## Application Framework ## Building a business from scratch — day 15 ## Learning the Hard Way: Microcontrollers for Newbs — the Martin files  ## [Learn Open Source] Learn install-pkg in 5 minutes ## How JavaScript works: the factory design pattern + 4 use cases ## What is NodeJS? 