DEV Community

Discussion on: Advent of Code 2020 Solution Megathread - Day 2: Password Philosophy

Collapse
 
rpalo profile image
Ryan Palo

Ooh boy it's a little early in the season to already be stumped by an off-by-one bug. 1-BASED INDICES RYAN! 1-BASED! IT'S RIGHT THERE IN THE PROMPT!

Also, I started a little parsing module to help me with some of the repetitive tedia.

Day2.h:

/// Day 2: Password Philosophy 
/// 
/// Find the passwords that aren't compliant.

#include <stdlib.h>
#include <stdint.h>

/// A Policied Password is a password that is accompanied by a policy
/// consisting of two positive integers and a letter.  These components
/// can be used to validate the password.
typedef struct {
  int a;
  int b;
  char letter;
  char password[30];
} PoliciedPassword;

/// Part 1 calculates how many valid passwords there are.
/// A password is valid if the # of occurrences of 'letter' is between
/// 'a' and 'b,' inclusive.
///
/// passes: the list of policied passwords to check
/// count: the number of passwords to check
int part1(PoliciedPassword** passes, size_t count);

/// Part 2 calculates how many valid passwords there are.
/// A password is valid of either the 'a' index or 'b' index character 
/// (1-based!) is equal to 'letter,' but not both.
int part2(PoliciedPassword** passes, size_t count);

/// day2 runs both parts in sequence and outputs their results.
int day2();
Enter fullscreen mode Exit fullscreen mode

Day2.c:

/// Day 2: Password Philosophy 
/// 
/// Find the passwords that aren't compliant.

#include "Day2.h"
#include "parsing.h"

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/// Parse the input file,
static PoliciedPassword** parse(size_t* lines) {
  FILE* fp;
  fp = fopen("data/day2.txt", "r");
  if (fp == NULL) {
    printf("Couldn't open input file.\n");
    exit(EXIT_FAILURE);
  }

  *lines = count_lines(fp);

  PoliciedPassword** passes = malloc(sizeof(PoliciedPassword*) * *lines);

  for (size_t i = 0; i < *lines; i++) {
    PoliciedPassword* p = malloc(sizeof(PoliciedPassword));
    fscanf(fp, "%d-%d %c: %s\n", &p->a, &p->b, &p->letter, p->password);
    passes[i] = p;
  }

  fclose(fp);
  return passes;
}

static void freePoliciedPasswordList(PoliciedPassword** passes, size_t count) {
  for (size_t i = 0; i < count; i++) {
    free(passes[i]);
    passes[i] = NULL;
  }
  free(passes);
}

int part1(PoliciedPassword** passes, size_t count) {
  size_t valid = 0;

  for (size_t i = 0; i < count; i++) {
    PoliciedPassword* p = passes[i];
    uint8_t matches = 0;
    for (size_t j = 0; p->password[j]; j++) {
      if (p->password[j] == p->letter) matches++;
    }
    if (p->a <= matches && matches <= p->b) valid++;
  }
  return valid;
}

int part2(PoliciedPassword** passes, size_t count) {
  size_t valid = 0;

  for (size_t i = 0; i < count; i++) {
    PoliciedPassword* p = passes[i];
    int matches = 0;
    if (p->password[p->a - 1] == p->letter) matches++;
    if (p->password[p->b - 1] == p->letter) matches++;
    if (matches == 1) valid++;
  }
  return valid;
}

int day2() {
  size_t count;
  PoliciedPassword** passes = parse(&count);
  printf("====== Day 2 ======\n");
  printf("Part 1: %d\n", part1(passes, count));
  printf("Part 2: %d\n", part2(passes, count));
  freePoliciedPasswordList(passes, count);
  return EXIT_SUCCESS;
}
Enter fullscreen mode Exit fullscreen mode

parsing.h:

#ifndef AOC2020_PARSING_H
#define AOC2020_PARSING_H

#include <stdio.h>
#include <stdlib.h>

/// Counts the number of newline characters in a text file.
/// Assumes no newline at the end of the last line (so adds +1 more)
size_t count_lines(FILE* fp);

#endif
Enter fullscreen mode Exit fullscreen mode

parsing.c:

#include "parsing.h"

#include <stdlib.h>
#include <stdio.h>

size_t count_lines(FILE* fp) {
  size_t lines = 0;
  while (!feof(fp)) {
    if (getc(fp) == '\n') lines++;
  }

  rewind(fp);
  return lines + 1;
}
Enter fullscreen mode Exit fullscreen mode

Also a little tooling to help generate the daily files:

bin/newday:

#!/usr/bin/env bash

# newday: Creates a new day of files for the Advent of Code Challenge
#
# Date:   12/2/2020
# Author: Ryan Palo

function usage() {
  echo "usage: newday NUMBER"
  echo
  echo "    NUMBER: The number of the day to create.  Will be inserted"
  echo "            into the templates and used for filenames."
}

function help() {
  echo "newday: Creates a new day of files for the Advent of Code challenge."
  echo
  usage
  echo
}

function make_day() {
  day="$1"
  echo "Creating Day ${day}." >&2
  sed "s/{X}/$day/g" "templates/DayX.c" > "src/Day${day}.c"
  sed "s/{X}/$day/g" "templates/DayX.h" > "src/Day${day}.h"
  sed "s/{X}/$day/g" "templates/TestDayX.c" > "test/TestDay${day}.c"
  sed "s/{X}/$day/g" 'templates/main.c' > 'src/main.c'
  touch "data/day${day}.txt"
  echo "Complete." >&2
}

function main() {
  if [[ "$#" -ne 1 ]]; then
    usage
    exit 1
  fi

  if [[ "$1" == '-h' ]]; then
    help
    exit 0
  fi

  if ! [[ "$1" =~ [:digit:]+ ]]; then
    echo "Input must be a number"
    exit 1
  fi

  make_day "$1"
}

main "$@"
Enter fullscreen mode Exit fullscreen mode