Computational Recruiting
4 minutos de lectura
Se nos proporciona un archivo de texto grande llamado data.txt
:
$ head data.txt
============ ============== ======== ========= ========== =========== ======== =================
First Name Last Name Health Agility Charisma Knowledge Energy Resourcefulness
============ ============== ======== ========= ========== =========== ======== =================
Alis Reeson 2 5 5 8 7 10
Gerri Bielfelt 8 9 3 8 5 9
Wolfie Appleby 5 1 2 7 2 1
Krishnah Minker 1 7 7 10 6 5
Cassondra Peizer 9 8 8 2 6 4
Jamie Aston 10 7 4 1 5 9
$ wc -l data.txt
204 data.txt
Y también tenemos una instancia de remota a la que conectarnos:
$ nc 94.237.55.39 38975
You will be given a file with N = 200 different potential candidates. Every candidates has 6 different skills, with a score 1 <= s <= 10 for each.
The formulas to calculate their general value are:
<skill>_score = round(6 * (int(s) * <skill>_weight)) + 10
overall_value = round(5 * ((health * 0.18) + (agility * 0.20) + (charisma * 0.21) + (knowledge * 0.08) + (energy * 0.17) + (resourcefulness * 0.16)))
Note: The round() function here is Python 3's round(), which uses a concept called Banker's Rounding
The weights for the 6 skills are: health_weight = 0.2, agility_weight = 0.3, charisma_weight = 0.1, knowledge_weight = 0.05, energy_weight = 0.05, resourcefulness_weight = 0.3
Enter the first 14 candidates ordered in the highest overall values.
Enter them like so: Name_1 Surname_1 - score_1, Name_2 Surname_2 - score_2, ..., Name_i Surname_i - score_i
e.g. Timothy Pempleton - 94, Jimmy Jones - 92, Randolf Ray - 92, ...
>
Como se puede ver, tenemos algunas fórmulas para calcular <skill>_score
y overall_value
, donde <skill>
es una de:
health
agility
charisma
knowledge
energy
resourcefulness
También hay pesos para cada habilidad:
health_weight = 0.2
agility_weight = 0.3
charisma_weight = 0.1
knowledge_weight = 0.05
energy_weight = 0.05
resourcefulness_weight = 0.3
Además, se nos dice que usemos Python debido a la función round
built-in. Necesitamos ingresar los primeros 14 candidatos ordenados según valores generales más altos, utilizando el formato proporcionado:
Name_1 Surname_1 - score_1, Name_2 Surname_2 - score_2, ..., Name_i Surname_i - score_i
Solución
El siguiente código utilizará una expresión regular para analizar todas las líneas del archivo y guardar todas las estadísticas con una clase Candidate
:
regex = r'^\s+(\w+?)\s+(\w+?)\s+(\d+?)\s+(\d+?)\s+(\d+?)\s+(\d+?)\s+(\d+?)\s+(\d+?)\s+$'
candidates = []
with open('data.txt') as f:
while (line := f.readline()):
matches = re.search(regex, line)
if not matches:
continue
first_name = matches[1]
last_name = matches[2]
health = int(matches[3])
agility = int(matches[4])
charisma = int(matches[5])
knowledge = int(matches[6])
energy = int(matches[7])
resourcefulness = int(matches[8])
candidates.append(Candidate(
first_name, last_name, health, agility, charisma, knowledge, energy, resourcefulness,
))
Esta clase Candidate
está definida aquí:
health_weight = 0.2
agility_weight = 0.3
charisma_weight = 0.1
knowledge_weight = 0.05
energy_weight = 0.05
resourcefulness_weight = 0.3
class Candidate:
def __init__(self,
first_name: str,
last_name: str,
health: int,
agility: int,
charisma: int,
knowledge: int,
energy: int,
resourcefulness: int):
self.first_name = first_name
self.last_name = last_name
self.health = health
self.agility = agility
self.charisma = charisma
self.knowledge = knowledge
self.energy = energy
self.resourcefulness = resourcefulness
def overall(self) -> int:
health_score = round(6 * self.health * health_weight) + 10
agility_score = round(6 * self.agility * agility_weight) + 10
charisma_score = round(6 * self.charisma * charisma_weight) + 10
knowledge_score = round(6 * self.knowledge * knowledge_weight) + 10
energy_score = round(6 * self.energy * energy_weight) + 10
resourcefulness_score = round(6 * self.resourcefulness * resourcefulness_weight) + 10
return round(5 * ((health_score * 0.18) + (agility_score * 0.20) + (charisma_score * 0.21) + (knowledge_score * 0.08) + (energy_score * 0.17) + (resourcefulness_score * 0.16)))
Hemos definido un método llamado overall
para calcular el valor de overall_value
de un candidato, usando cada <skill>_score
.
Finalmente, solo necesitamos llamar a overall
en cada uno de los candidatos y ordenarlos según este resultado. Una vez que lo tenemos, podemos obtener la solución en el formato de texto dado:
candidates.sort(key=lambda c: c.overall(), reverse=True)
solution = ', '.join(
f'{c.first_name} {c.last_name} - {c.overall()}' for c in candidates[:14]
)
Lo único que debemos hacer ahora es conectarnos a la instancia remota e ingresar la solución:
host, port = sys.argv[1].split(':')
io = remote(host, port)
io.sendlineafter(b'> ', solution.encode())
io.success(io.recvline().decode())
Flag
Con todo esto, tenemos la flag:
$ python3 solve.py 94.237.55.39:38975
[+] Opening connection to 94.237.55.39 on port 38975: Done
[+] You have recruited the best possible companions. Before you leave, take this: HTB{t3xT_p4rS1ng_4nD_maTh_f0rmUl4s...}
[*] Closed connection to 94.237.55.39 port 38975
El script completo se puede encontrar aquí: solve.py
.