Advent of Code 2024 | Dia 2
Prólogo:
- Nesses últimos dias andei estudando algumas coisas fora do escopo da programação, portanto não fui atrás até ontem de voltar a praticar Rust usando o Advent of Code (2024).
- Esse segundo dia aparentava ser muito mais complexo do que o primeiro, porém me faltava um pouco de conhecimento com a sintaxe da linguagem para executar a resolução dos problemas de forma correta e incisiva; então esse problema serviu muito bem para me ensinar conceitos específicos de sorting evolvendo arrays usando sinatxes específicas da linguagem, como será visto no tópico seguinte.
Desafio 1 (dia 2):
— Day 2: Red-Nosed Reports —
| Sequence |
|---|
| 7 6 4 2 1 |
| 1 2 7 8 9 |
| 9 7 6 2 1 |
| 1 3 2 4 5 |
| 8 6 4 4 1 |
| 1 3 6 7 9 |
This example data contains six reports each containing five levels.
The engineers are trying to figure out which reports are safe. The Red-Nosed reactor safety systems can only tolerate levels that are either gradually increasing or gradually decreasing. So, a report only counts as safe if both of the following are true:
The levels are either all increasing or all decreasing.
Any two adjacent levels differ by at least one and at most three.
In the example above, the reports can be found safe or unsafe by checking those rules:
| Sequence | Status | Reason |
|---|---|---|
| 7 6 4 2 1 | Safe | The levels are all decreasing by 1 or 2. |
| 1 2 7 8 9 | Unsafe | 2 7 is an increase of 5. |
| 9 7 6 2 1 | Unsafe | 6 2 is a decrease of 4. |
| 1 3 2 4 5 | Unsafe | 1 3 is increasing but 3 2 is decreasing. |
| 8 6 4 4 1 | Unsafe | 4 4 is neither an increase or a decrease. |
| 1 3 6 7 9 | Safe | The levels are all increasing by 1, 2, or 3. |
So, in this example, 2 reports are safe.
Analyze the unusual data from the engineers. How many reports are safe?
- Your puzzle answer was 585.
Evoluções no código:
- De primeira, consegui fazer um bom código, porém ele estava muito semelhante á qualquer código de linguagem C, eu sentia que precisava entender melhor as funções de cada tipo dentro da linguagem, no meu caso eu me aprofundei mais na manipulação de vetores através do uso de pares dinâmicos usando
windows(função dentro da linguagem que permite sectarizar o array em pares e calcular uma determinada condição, dentro do par que está sendo iterado).
- Exemplo de código antes de aplicar a função:
fn is_safe(report: &Vec<i32>) -> Result<bool, Error> {
let mut increasing: bool = true;
let mut decreasing: bool = true;
for i in 0..report.len() - 1 {
if report[i] < report[i + 1] {
decreasing = false;
}
if report[i] > report[i + 1] {
increasing = false;
}
}
Ok(increasing || decreasing)
}
- Existe uma observação na qual eu gostaria de fazer:
O código está bem legível, porém qualquer outra linguagem é capaz de fazer da forma na qual eu fiz, então qual o intuito de usar o Rust se eu não conheço as peculiaridades da linguagem?
- Então decidi fuçar um pouco aplicações desse problema no qual estava fazendo e vi que muitas pessoas usam funções dentro da biblioteca
std::sliceda linguagem, que oferecem algumas formas de manipulação de vetores e arrays por meio de métodos já implementados, diminuindo o tamanho do código.
- Reescrevendo o código de cima, agora aplicando o que foi aprendido:
fn is_safe(report: &Vec<i32>) -> Result<bool, Error> {
let is_increasing = report.windows(2).all(|num| num[1] > num[0]);
let is_decreasing = report.windows(2).all(|num| num[0] > num[1]);
if !is_increasing && !is_decreasing {
return Ok(false);
}
Ok(report.windows(2).all(|num| (num[1] - num[0]).abs() <= 3))
}
Foi usado a função
windows, que me permite iterar o vetor por meio de pares(o tamanho pode ser definido como parâmetro), e dentro disso eu posso verificar uma condição booleana usando esses pares que foram escolhidos; nesse caso eu verifiquei se os números eram crescentes ou decrescentes, além de também verificar se a diferença desses números era menor ou maior que 3, como foi pedido pelo exercício.Assim, verificando o input dado pelo desafio, temos o resultado:
585.
Desafio 2 (dia 2):
- Agora preciso deixar passar apenas um erro contido em cada report dentro do input, no exemplo temos:
| Sequence | Status | Reason |
|---|---|---|
| 7 6 4 2 1 | Safe | Safe without removing any level. |
| 1 2 7 8 9 | Unsafe | Unsafe regardless of which level is removed. |
| 9 7 6 2 1 | Unsafe | Unsafe regardless of which level is removed. |
| 1 3 2 4 5 | Safe | Safe by removing the second level, 3. |
| 8 6 4 4 1 | Safe | Safe by removing the third level, 4. |
| 1 3 6 7 9 | Safe | Safe without removing any level. |
- Esse problema é um problema clássico de fila, onde vamos removendo o primeiro elemento, e re-validando o array á cada remoção, repetindo o processo caso se confirme inválido novamente.
fn is_report_safe(report: &Vec<i32>, use_dampener: bool) -> Result<bool, Error> {
// verificando validade do report
// antes de executar a primeira remoção
if is_safe(report)? {
return Ok(true);
}
if use_dampener {
for i in 0..report.len() {
let mut report_copy = report.clone();
// esse elemento só será removido caso o array
// não se confirme válido no primeiro if
report_copy.remove(i);
if is_safe(&report_copy)? {
return Ok(true);
}
}
}
// caso após todas as iterações o array continue inválido
// ele sai do escopo e retorna falso
Ok(false)
}
- Assim, verificando o input dado pelo desafio, temos o resultado:
626.
Considerações finais:
- Achei esse problema muito bem pensado, tive que fuçar bastante pra entender que podia apenas usar uma fila para verificar a validade do array, sem fazer um malabarismo enorme; também me lembrou o quanto eu preciso reforçar meu conhecimento em estruturas de dados e algoritmos, para resolver problemas do tipo de forma mais dinâmica.
