Ryan is an engineer in the Sacramento Area with a focus in Python, Ruby, and Rust. Bash/Python Exercism mentor. Coding, physics, calculus, music, woodworking. Looking for work!
This was a long one, but I learned a ton about the <string.h> library. I also incremented a macro by 1 and spent 3 hours chasing a segfault. So, all in all, I think it sounds like we all had similar days. There are also several edge cases that I'm pretty sure my code doesn't cover, BUT that doesn't matter because this is AoC and it's not wrong if you get the stars. Right? Right?
Day4.h:
#ifndef AOC2020_DAY4_H
#define AOC2020_DAY4_H
/// Day 4: Passport Processing////// Pick through passports made up of key/value pairs to figure out/// which ones are valid.#include <stdlib.h>
#include <stdbool.h>
#define NUM_PASS_FIELDS 8 ///< Number of fields in a SimplePassport
#define MAX_ENTRY_SIZE 20 ///< Max size of possible value in key/value
#define KEY_SIZE 4 ///< Size of a key in key/value
#define HAIR_COLOR_SIZE 8 ///< Size of a well-formed hair color
#define PID_SIZE 10 ///< Size of a well-formed PID
/// A simple passport just has certain fields or not. Valid ones have/// them all, with 'cid' being optional.typedefstruct{boolbyr;booliyr;booleyr;boolhgt;boolhcl;boolecl;boolpid;boolcid;}SimplePassport;/// Parses the input file, which is a series of passports. Each key/val/// is separated by a space or newline. Passports are separated by /// two newlines. Returns a list of passport structs.SimplePassport*parse_simple_passports(constchar*filename,int*count);/// Counts the number of valid passports. Passports are valid if/// the have all keys, except CID is optional.intpart1(SimplePassport*passes,intcount);// ============== Part 2 ================== ///// Possible values for height units: none, inches, or centimeters.typedefenum{HT_BAD,HT_IN,HT_CM,}HeightUnit;/// A height is a measurement associated with a set of units.typedefstruct{intvalue;HeightUnitunits;}Height;/// Possible options for eye colortypedefenum{EYE_BAD,EYE_AMBER,EYE_BLUE,EYE_BROWN,EYE_GREY,EYE_GREEN,EYE_HAZEL,EYE_OTHER,}EyeColor;/// A fancy passport has strict value validation for all fields./// Note: all fields here must be present to win.typedefstruct{/// Birth Year: 1920 <= x <= 2002intbyr;/// Issue Year: 2010 <= x <= 2020intiyr;/// Expiration Year: 2020 <= x <= 2030inteyr;/// Height: 150 <= cm <= 193 || 59 <= in <= 76Heighthgt;/// Hair Color: # followed by 6 chars 0-9 or a-fcharhcl[8];/// Eye Color: amb | blu | brn | gry | grn | hzl | othEyeColorecl;/// Passport ID : Exactly 9 digits. (Storing 10 to find invalid ones)charpid[10];}FancyPassport;/// Parse fancy passports from an input file. The number of them/// is stored in 'count'.FancyPassport*parse_fancy_passports(constchar*filename,int*count);/// Counts the number of valid fancy passports based on the rules above.intpart2(FancyPassport*passes,intcount);/// Runs both parts.intday4(void);#endif
Day4.c:
#include "Day4.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/// Loops through a file and counts all instances of double-newlines./// Adds one at the end for the last entry which doesn't have a trailing/// double-newline.staticintcount_passports(FILE*fp){intcount=0;while(!feof(fp)){if(getc(fp)=='\n'&&getc(fp)=='\n')count++;}count++;// No double newline after last one.rewind(fp);returncount;}/// Parses a single passport from a file. Leaves the file pointer /// after the trailing double-newline, ready to parse the next one.staticSimplePassportparse_simple_passport(FILE*fp){charkey[4];charvalue[20];SimplePassportpassport={0};while(!feof(fp)&&getc(fp)!='\n'){memset(key,0,4);memset(value,0,20);fseek(fp,-1,SEEK_CUR);// Un-eat the previous char because we need it.fgets(key,KEY_SIZE,fp);fscanf(fp,"%s",value);// Eat the colon, valuegetc(fp);// Eat white space?if(strcmp(key,"byr")==0)passport.byr=true;elseif(strcmp(key,"iyr")==0)passport.iyr=true;elseif(strcmp(key,"eyr")==0)passport.eyr=true;elseif(strcmp(key,"hgt")==0)passport.hgt=true;elseif(strcmp(key,"hcl")==0)passport.hcl=true;elseif(strcmp(key,"ecl")==0)passport.ecl=true;elseif(strcmp(key,"pid")==0)passport.pid=true;elseif(strcmp(key,"cid")==0)passport.cid=true;else{printf("Unrecognized key: %s\n",key);}}returnpassport;}SimplePassport*parse_simple_passports(constchar*filename,int*count){FILE*fp;fp=fopen(filename,"r");if(fp==NULL){printf("Couldn't open file.\n");exit(EXIT_FAILURE);}*count=count_passports(fp);SimplePassport*passes=(SimplePassport*)malloc(sizeof(SimplePassport)**count);for(inti=0;i<*count;i++){passes[i]=parse_simple_passport(fp);}fclose(fp);returnpasses;}intpart1(SimplePassport*passes,intcount){intinvalid=0;for(inti=0;i<count;i++){bool*p=(bool*)&passes[i];// Iterates over each field of the passport by using the bytes of// the struct as an array of bools. Which should be OK?for(intj=0;j<NUM_PASS_FIELDS-1;j++){if(p[j]==false){invalid++;break;};}}returncount-invalid;}// ================= Part 2 ===================== ///// Parses a height string to a Height struct. Well-formed strings/// are [0-9]+(in|cm). If it's invalid, leaves the value as 0 and/// the units as HT_BAD.staticHeightparse_height(char*value){Heighth;intpossible_value;charunits[4];sscanf(value,"%d%s",&possible_value,units);if(strcmp(units,"in")==0){h.value=possible_value;h.units=HT_IN;}elseif(strcmp(units,"cm")==0){h.value=possible_value;h.units=HT_CM;}else{h.value=0;h.units=HT_BAD;}returnh;}/// Parses an EyeColor from a string.staticEyeColorparse_eye_color(char*value){if(strcmp(value,"amb")==0)returnEYE_AMBER;if(strcmp(value,"blu")==0)returnEYE_BLUE;if(strcmp(value,"brn")==0)returnEYE_BROWN;if(strcmp(value,"gry")==0)returnEYE_GREY;if(strcmp(value,"grn")==0)returnEYE_GREEN;if(strcmp(value,"hzl")==0)returnEYE_HAZEL;if(strcmp(value,"oth")==0)returnEYE_OTHER;returnEYE_BAD;}/// Prints out a FancyPassport for debugging.staticvoidprint_fancy_passport(FancyPassport*p){printf("Passport:\n");printf("BYR: %d\nIYR: %d\nEYR: %d\n",p->byr,p->iyr,p->eyr);printf("HGT: %d-%d\nHCL: %s\nECL: %d\n",p->hgt.value,p->hgt.units,p->hcl,p->ecl);printf("PID: %s\n",p->pid);}/// Parses a single fancy passport from a file.staticFancyPassportparse_fancy_passport(FILE*fp){charkey[KEY_SIZE];charvalue[20];FancyPassportpassport={0};while(!feof(fp)&&getc(fp)!='\n'){memset(key,0,4);memset(value,0,20);fseek(fp,-1,SEEK_CUR);// Un-eat the previous char because we need it.fgets(key,KEY_SIZE,fp);fscanf(fp,":%s",value);// Eat the colon, valuegetc(fp);// Eat white spaceif(strcmp(key,"byr")==0)passport.byr=atoi(value);elseif(strcmp(key,"iyr")==0)passport.iyr=atoi(value);elseif(strcmp(key,"eyr")==0)passport.eyr=atoi(value);elseif(strcmp(key,"hgt")==0)passport.hgt=parse_height(value);elseif(strcmp(key,"hcl")==0){if(strlen(value)!=HAIR_COLOR_SIZE-1)continue;// Leave it emptystrncpy(passport.hcl,value,HAIR_COLOR_SIZE-1);}elseif(strcmp(key,"ecl")==0)passport.ecl=parse_eye_color(value);elseif(strcmp(key,"pid")==0){if(strlen(value)!=PID_SIZE-1)continue;// Leave it emptystrncpy(passport.pid,value,PID_SIZE-1);}elseif(strcmp(key,"cid")==0)continue;else{printf("Unrecognized key: %s\n",key);}}returnpassport;}FancyPassport*parse_fancy_passports(constchar*filename,int*count){FILE*fp;fp=fopen(filename,"r");if(fp==NULL){printf("Couldn't open file.\n");exit(EXIT_FAILURE);}*count=count_passports(fp);FancyPassport*passes=(FancyPassport*)malloc(sizeof(FancyPassport)**count);for(inti=0;i<*count;i++){passes[i]=parse_fancy_passport(fp);}fclose(fp);returnpasses;}/// Checks whether a hair color is valid.staticboolvalid_hair_color(char*color){if(color[0]!='#')returnfalse;for(inti=1;color[i];i++){if((color[i]<'0'||color[i]>'9')&&(color[i]<'a'||color[i]>'f')){returnfalse;}}returntrue;}/// Checks whether a PID is valid.staticboolvalid_pid(char*value){if(strlen(value)!=PID_SIZE-1)returnfalse;for(inti=0;value[i];i++){if(value[i]<'0'||value[i]>'9')returnfalse;}returntrue;}/// Finger-saving macro for part2: increment the invalids and continue#define INC_BAD {invalid++; continue;}
intpart2(FancyPassport*passes,intcount){intinvalid=0;for(inti=0;i<count;i++){FancyPassportp=passes[i];if(p.byr<1920||p.byr>2002)INC_BADif(p.iyr<2010||p.iyr>2020)INC_BADif(p.eyr<2020||p.eyr>2030)INC_BADif(p.hgt.units==HT_BAD)INC_BADif(p.hgt.units==HT_CM&&(p.hgt.value<150||p.hgt.value>193))INC_BADif(p.hgt.units==HT_IN&&(p.hgt.value<59||p.hgt.value>76))INC_BADif(!valid_hair_color(p.hcl))INC_BADif(p.ecl==EYE_BAD)INC_BADif(!valid_pid(p.pid))INC_BAD}returncount-invalid;}intday4(){intcount;SimplePassport*passes=parse_simple_passports("data/day4.txt",&count);printf("====== Day 4 ======\n");printf("Part 1: %d\n",part1(passes,count));free(passes);count=0;FancyPassport*passes2=parse_fancy_passports("data/day4.txt",&count);printf("Part 2: %d\n",part2(passes2,count));free(passes2);returnEXIT_SUCCESS;}
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
This was a long one, but I learned a ton about the
<string.h>
library. I also incremented a macro by 1 and spent 3 hours chasing a segfault. So, all in all, I think it sounds like we all had similar days. There are also several edge cases that I'm pretty sure my code doesn't cover, BUT that doesn't matter because this is AoC and it's not wrong if you get the stars. Right? Right?Day4.h:
Day4.c: