Published on

La Última Defensa de la "Valiant" - Interview challenge solution

Authors

This challenge belongs to a series of programming challenges published by AltScore. You can see the complete list here. This is the fifth challenge.

Problem Statement

La Última Defensa de la "Valiant" - ¡Cuenta Regresiva!

Situación

La nave insignia "Valiant", orgullo de la flota estelar, está bajo asedio. Una nave enemiga, veloz y letal, se aproxima con una intención clara: interceptar y destruir a la nave de suministros "Hope", que se encuentra en una posición vulnerable. El puesto de defensa de la "Valiant", crucial para repeler la amenaza, ha sufrido daños críticos en el ataque. La pantalla del radar, que normalmente mostraría la posición de la nave enemiga, es una maraña de estática y distorsiones. Sin embargo, el puerto de comunicaciones de la consola aún funciona, transmitiendo datos en texto sin formato. Afortunadamente, se ha encontrado un manual de instrucciones parcialmente legible que describe el formato de estos datos. Además, tienes acceso a la bitácora del operador anterior, donde encuentras la última lectura de la batalla anterior junto a una captura de pantalla del radar, proporcionando información valiosa sobre la situación inicial.

Misión

Tu misión es crucial: detener a la nave enemiga antes de que alcance su objetivo. Dispones de un solo disparo, una descarga de energía concentrada capaz de aniquilar cualquier nave en un instante. El disparo debe ser preciso, dirigido a las coordenadas exactas (x, y) donde se encontrará la nave enemiga en el momento del impacto.

El Desafío

La nave enemiga se mueve con una agilidad sorprendente, cambiando de dirección y velocidad de forma inesperada. Su objetivo es interceptar a la "Hope" lo antes posible, por lo que elegirá la ruta más directa y eficiente, evitando los obstáculos que se encuentran en el espacio de batalla. El espacio de acción de la torre de defensa es limitado y plagado de estos obstáculos. Tienes un total de 4 turnos para completar tu misión. En cada turno, puedes elegir entre dos acciones:

  • Leer el radar: Acceder al puerto de comunicaciones y obtener una lectura textual de la posición actual de la nave enemiga, en el formato descrito en el manual.
  • Atacar: Disparar tu arma a las coordenadas especificadas (x, y), teniendo en cuenta que la nave enemiga se habrá movido desde la última lectura del radar. Recuerda que solo tienes un disparo.

Cuenta Regresiva

¡El tiempo es esencial! Desde el momento en que inicies la batalla llamando a la API de "start", tendrás solo 10 minutos para analizar los datos, predecir el movimiento de la nave enemiga considerando los obstáculos, y ejecutar el ataque.

Objetivo

Escribe un programa que, bajo la presión del tiempo, analice los datos del puerto de comunicaciones en el formato especificado, utilice la información de la bitácora para comprender la situación inicial, prediga el movimiento de la nave enemiga considerando los obstáculos, y determine el momento y las coordenadas precisas para lanzar el ataque. ¡El destino de la "Hope" y de la "Valiant" está en tus manos!

Pistas

  • El espacio de acción es una cuadrícula de 8x8, identificada desde "a1" hasta "h8".
  • La nave enemiga se representa con el carácter "^".
  • La nave amiga "Hope" se representa con el carácter "#".
  • Los obstáculos se representan con el carácter "$".
  • El espacio no ocupado se representa con el carácter "0".
  • Un salto de línea en los datos del radar se indica con el carácter "|".
  • La nave enemiga se mueve con un patrón peculiar, pero predecible si lo observas con atención.
  • Tienes 4 turnos.
  • Puedes "leer" o "atacar" en cada turno.
  • Solo tienes un disparo.
  • La nave enemiga busca la intercepción más rápida, evitando obstáculos.
  • Tienes 10 minutos para hacer tu disparo, luego de esto no podrás volver a intentar.
  • Tienes acceso a la última lectura del radar y una captura de pantalla de la batalla anterior en la bitácora.

Bitácora

Última lectura del radar:

a01b01c01d01e01f01g01h01|a02b02c02d02e$2f02g02h02|a03b03c03d03e03f03g03h$3|a04b04c04d04e04f04g04h04|a05b05c05d05e$5f05g^5h05|a06b06c06d06e$6f06g06h06|a07b07c07d07e07f07g07h07|a08b08c08d08e08f#8g08h08|

Captura de pantalla correspondiente a la lectura del radar:

0 0 0 0 0 # 0 0
0 0 0 0 0 0 0 0
0 0 0 0 $ 0 0 0
0 0 0 0 $ 0 ^ 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 $
0 0 0 0 $ 0 0 0
0 0 0 0 0 0 0 0

Recursos

Solution

I DID NOT solve this challenge. I made some mistakes reading the radar that I was not aware of, and the time was running out. I'm going to share my thought process and the code I wrote to try to solve it.

Understanding how to build the matrix from the radar

The radar is a string that contains the position of the enemy ship and the obstacles in some format that we need to understand.

My process was:

  • It looks like the | character is used to separate the rows of the matrix
  • a, b, c, d, e, f, g, h are the columns or rows
  • 1, 2, 3, 4, 5, 6, 7, 8 are the columns or rows
  • Try to find a pattern for the first part of the string a01b01c01d01e01f01g01h01 in the matrix
  • Not much to understand, nothing special, let's try with another part of the string a02b02c02d02e$2f02g02h02
  • Ok, we can see the string has a $ and the matrix has some rows that contain a $ character
  • You can see each part separated by the pipe has the same letters but different numbers, from 1 to 8, and the matrix is an 8x8 grid
  • The part a02b02c02d02e$2f02g02h02 has the number 2, it can be vertical or horizontal
  • Let's try with the second row bottom to top because the second row from top to bottom doesn't have the $ character
  • We need to get 0 0 0 0 $ 0 0 0 from a02b02c02d02e$2f02g02h02
  • If we remove the letters we get 02020202$2020202
  • If we remove the number 2 we get 0000$000 -> add some spaces and we get 0 0 0 0 $ 0 0 0

So, each part of the string is a row of the matrix from bottom to top, the numbers are the rows and the letters are the columns.

Time to write the code!

Implement the radar parsing logic

Ill implement some "fancy" transformation from letters to columns x). Ty charCodeAt for the win!

function parseRadar(radarData) {
  const matrix = Array(8)
    .fill()
    .map(() => Array(8).fill('0'))

  // Split into rows
  const rows = radarData.split('|').filter((row) => row.length > 0)

  rows.forEach((row) => {
    // Process each cell (3 characters each)
    for (let i = 0; i < row.length; i += 3) {
      const cell = row.slice(i, i + 3)
      const colCoord = cell[0].charCodeAt(0) - 97 // Convert a-h to 0-7 (columns)
      const content = cell[1]
      const rowCoord = 8 - parseInt(cell[2]) // Convert 1-8 to 7-0 (rows in reverse)

      matrix[rowCoord][colCoord] = content
    }
  })

  return matrix.map((row) => row.join(' '))
}

We can now use the parseRadar function to get the matrix from the radar data.

Lets call the function.

const radarData =
  'a01b01c01d01e01f01g01h01|a02b02c02d02e$2f02g02h02|a03b03c03d03e03f03g03h$3|a04b04c04d04e04f04g04h04|a05b05c05d05e$5f05g^5h05|a06b06c06d06e$6f06g06h06|a07b07c07d07e07f07g07h07|a08b08c08d08e08f#8g08h08|'

const result = parseRadar(radarData)

const columnHeaders =
  '  ' + Array.from({ length: 8 }, (_, i) => String.fromCharCode(65 + i)).join(' ')
console.log('Radar Matrix (8x8):')
console.log(columnHeaders)
console.log('-----------------')

result.forEach((row, index) => {
  console.log(`${index + 1} ${row}`)
})

And the output is:

Radar Matrix (8x8):
  A B C D E F G H
-----------------
1 0 0 0 0 0 # 0 0
2 0 0 0 0 0 0 0 0
3 0 0 0 0 $ 0 0 0
4 0 0 0 0 $ 0 ^ 0
5 0 0 0 0 0 0 0 0
6 0 0 0 0 0 0 0 $
7 0 0 0 0 $ 0 0 0
8 0 0 0 0 0 0 0 0

Which is exactly as the example given in the problem statement.

Let's start the game

To start the game we need to call the start endpoint.

curl -X 'POST' \
  'https://makers-challenge.altscore.ai/v1/s1/e5/actions/start' \
  -H 'accept: application/json' \
  -H 'API-KEY: API-KEY' \
  -d ''

The clock is now running, we have 10 minutes to shoot the enemy ship.

Checking at the docs there is another endpoint to read the radar and to attack the enemy ship.

Remember we only have 4 turns, so we need to be careful and use the radar to know the position of the enemy ship.

Each time we read the radar or make an attack counts as one turn, so we need to plan our moves carefully within our 4-turn limit. This is where I made the mistake, I was not aware that the radar query counts as one turn so I ended up calling radar twice and wasting one turn.

Time to read the radar

curl -X 'POST' \
  'https://makers-challenge.altscore.ai/v1/s1/e5/actions/perform-turn' \
  -H 'accept: application/json' \
  -H 'API-KEY: API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "action": "radar",
  "attack_position": null
}'

This will return the radar data in the format we need to parse. When I tried to solve the challenge I did not save the radar read.

According to the docs we should get a response similar to:

{
  "performed_action": "radar",
  "turns_remaining": 3,
  "time_remaining": 10,
  "action_result": "something",
  "message": "RADAR_READ_HERE"
}

Using our script implemented above we can parse the radar data and get the matrix.

We need to find our ship and the enemy ship in the matrix.

  • Our ship is represented by the # character.
  • The enemy ship is represented by the ^ character.
  • Something important to notice is the response can also have obstacles represented by the $ character.
  • Not sure what to do, but I guess we cannot shoot through obstacles.

We need to predict the enemy ship movement:

  • Use the last 3 radar reads to predict the movement 👾
  • Make sure to take into account the obstacles 🧱
  • Then shoot 🔫

Attack the enemy ship

Let's imagine we know the enemy ship position in the next turn and the position is (a, 3). Remember X is the column and Y is the row. X goes from a to h and Y goes from 1 to 8.

So, if we want to shoot the enemy ship at the coordinates (a,3) we need to do:

curl -X 'POST' \
  'https://makers-challenge.altscore.ai/v1/s1/e5/actions/perform-turn' \
  -H 'accept: application/json' \
  -H 'API-KEY: API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "action": "attack",
  "attack_position": {
    "x": "a",
    "y": 3
  } 
}'

I guess if we hit the ship we will get a positive result and if we miss we will get a negative result and game over.

Since I was not able to complete the challenge, I'm getting the following response:

{
  "code": "ConflictError",
  "message": "conflict error",
  "details": {
    "message": "Turn limit reached, game over"
  }
}

TADA! 🎉 :(