Published on

La Infiltración en Ciudad Prisma - Interview challenge solution

Authors

TLDR:

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

Problem Statement

La Infiltración en Ciudad Prisma

En Ciudad Prisma, una metrópolis donde los humanos y los Pokémon conviven en armonía, se ha descubierto una conspiración. Un grupo de científicos ha estado recopilando datos sobre Pokémon para un proyecto misterioso.

Tu misión como agente infiltrado es acceder al sistema de datos de Ciudad Prisma y extraer información crucial: calcular la altura promedio de los Pokémon por cada tipo.

Los científicos utilizan el PokeAPI como su fuente de datos. Deberás:

  1. Acceder a la API de tipos de Pokémon
  2. Para cada tipo, obtener todos los Pokémon asociados
  3. Calcular la altura promedio por tipo
  4. Reportar tus hallazgos

Recursos:

  • PokeAPI - Base de datos de Pokémon
  • Envía tu respuesta aquí: [POST] /v1/s1/e6/solution
  • ¡No olvides consultar la Documentación!

Solution

Step 1: Get all Pokemon types

First, we need to fetch all available Pokemon types from the PokeAPI. The endpoint https://pokeapi.co/api/v2/type/ returns a list of all types with URLs to their detailed information.

I wrote a bash script to fetch all type data:

#!/bin/bash

# Output file will be created in the current directory
OUTPUT_FILE="6-pokemon-types.json"

TYPES_URL="https://pokeapi.co/api/v2/type/"
TYPES_JSON=$(curl -s "$TYPES_URL")

# Get type URLs using jq
TYPE_URLS=$(echo "$TYPES_JSON" | jq -r '.results[].url')

echo "[" > "$OUTPUT_FILE"

FIRST=true
for TYPE_URL in $TYPE_URLS; do
    TYPE_INFO=$(curl -s "$TYPE_URL")

    if [ "$FIRST" = true ]; then
        FIRST=false
    else
        echo "," >> "$OUTPUT_FILE"
    fi

    echo "$TYPE_INFO" >> "$OUTPUT_FILE"

    sleep 1
done

echo "Data saved to $OUTPUT_FILE"

This script:

  • Fetches the list of all types
  • Iterates through each type URL
  • Downloads the full type information (including all Pokemon of that type)
  • Saves everything to a JSON file

Note: The output file is quite large (~500KB) as it contains all Pokemon for each type.

Step 2: Get heights for each Pokemon

Now we need to get the height of each Pokemon. The type data includes Pokemon URLs, so we can fetch each one.

import { writeFileSync } from 'fs'

async function processPokemon(name, url, typeId, typeName) {
  try {
    const response = await fetch(url)
    const pokemonData = await response.json()
    const height = pokemonData.height

    const csvLine = `${name}:${typeId}:${typeName}:${height}\n`
    writeFileSync('6-pokemon-heights.csv', csvLine, { flag: 'a' })

    console.log(`Processed ${name}...`)
  } catch (error) {
    console.error(`Error processing ${name}:`, error.message)
  }
}

const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))

async function main() {
  console.log('Processing Pokemon data...')

  writeFileSync('6-pokemon-heights.csv', '')

  try {
    const typesJson = await import('./6-pokemon-types.json', { assert: { type: 'json' } }).then(
      (m) => m.default
    )

    for (const type of typesJson) {
      const { id: typeId, name: typeName, pokemon } = type
      console.log(`Processing Pokemon for type: ${typeName} (ID: ${typeId})`)

      for (const pkmn of pokemon) {
        if (pkmn.pokemon) {
          const { name, url } = pkmn.pokemon
          await processPokemon(name, url, typeId, typeName)

          await delay(200)
        }
      }
    }

    console.log('Done! --> 6-pokemon-heights.csv')
  } catch (error) {
    console.error('Error D:', error.message)
  }
}

main()

This script:

  • Reads the types JSON file
  • For each type, iterates through all Pokemon
  • Fetches each Pokemon's data and extracts the height
  • Saves to a CSV file with format: name:typeId:typeName:height

Sample output from pokemon_heights.csv:

pidgey:1:normal:3
pidgeotto:1:normal:11
pidgeot:1:normal:15
rattata:1:normal:3
raticate:1:normal:7
...
charizard:10:fire:17
vulpix:10:fire:6
arcanine:10:fire:19
...

Step 3: Calculate the average height by type

Now we process the CSV to calculate averages:

import { readFileSync } from 'fs'

const csvContent = readFileSync('6-pokemon-heights.csv', 'utf-8')
const lines = csvContent.trim().split('\n')

const typeData = new Map()

for (const line of lines) {
  const [, , type, height] = line.split(':')
  const heightNum = parseFloat(height)

  if (!typeData.has(type)) {
    typeData.set(type, { sum: 0, count: 0 })
  }

  const data = typeData.get(type)
  data.sum += heightNum
  data.count += 1
}

// Calculate averages and format the result
const heights = {}
for (const [type, data] of typeData) {
  const average = (data.sum / data.count).toFixed(3)
  heights[type] = average
}

// Output the result
console.log(JSON.stringify({ heights }, null, 2))

The Result

Running the calculation script produces:

{
  "heights": {
    "bug": "19.529",
    "dark": "20.064",
    "dragon": "43.374",
    "electric": "16.491",
    "fairy": "19.410",
    "fighting": "22.600",
    "fire": "28.990",
    "flying": "16.597",
    "ghost": "14.728",
    "grass": "16.822",
    "ground": "19.323",
    "ice": "18.273",
    "normal": "15.665",
    "poison": "33.706",
    "psychic": "16.206",
    "rock": "18.020",
    "steel": "27.758"
  }
}

Interesting findings:

  • Dragon types are the tallest on average (43.374 decimeters = ~4.3 meters)
  • Poison types are surprisingly tall (33.706) - probably due to large snakes like Arbok and Seviper
  • Ghost types are the shortest (14.728)

Submit the answer

curl -X 'POST' \
  'https://makers-challenge.altscore.ai/v1/s1/e6/solution' \
  -H 'accept: application/json' \
  -H 'API-KEY: API-KEY' \
  -H 'Content-Type: application/json' \
  -d '{
  "heights": {
    "bug": "19.529",
    "dark": "20.064",
    "dragon": "43.374",
    "electric": "16.491",
    "fairy": "19.410",
    "fighting": "22.600",
    "fire": "28.990",
    "flying": "16.597",
    "ghost": "14.728",
    "grass": "16.822",
    "ground": "19.323",
    "ice": "18.273",
    "normal": "15.665",
    "poison": "33.706",
    "psychic": "16.206",
    "rock": "18.020",
    "steel": "27.758"
  }
}'

And the response is:

{
  "result": "correct"
}

TADA! 🎉