225 lines
7.6 KiB
C++
225 lines
7.6 KiB
C++
|
#include <iostream>
|
||
|
#include <fstream>
|
||
|
#include <vector>
|
||
|
#include <optional>
|
||
|
#include <variant>
|
||
|
|
||
|
class Passport {
|
||
|
std::optional<uint32_t> birthYear;
|
||
|
std::optional<uint32_t> issueYear;
|
||
|
std::optional<uint32_t> expireYear;
|
||
|
std::optional<float> height;
|
||
|
std::optional<std::string> hairColour;
|
||
|
std::optional<std::string> eyeColour;
|
||
|
std::optional<uint64_t> passportId;
|
||
|
std::optional<uint64_t> countryId;
|
||
|
|
||
|
public:
|
||
|
Passport() = default;
|
||
|
|
||
|
[[nodiscard]] bool isValid() const {
|
||
|
return birthYear.has_value()
|
||
|
&& issueYear.has_value()
|
||
|
&& expireYear.has_value()
|
||
|
&& height.has_value()
|
||
|
&& hairColour.has_value()
|
||
|
&& eyeColour.has_value()
|
||
|
&& passportId.has_value();
|
||
|
// && countryId.has_value();
|
||
|
}
|
||
|
|
||
|
Passport &withBirthYear(uint32_t year) {
|
||
|
this->birthYear = year;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withIssueYear(uint32_t year) {
|
||
|
this->issueYear = year;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withExpirationYear(uint32_t year) {
|
||
|
this->expireYear = year;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withHeightInCentimeters(uint32_t cm) {
|
||
|
this->height = (float) cm;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withHeightInInches(uint32_t inches) {
|
||
|
this->height = inches * 2.54f;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withHairColour(std::string colour) {
|
||
|
this->hairColour = colour;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withEyeColour(std::string colour) {
|
||
|
this->eyeColour = colour;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withPassportId(uint64_t id) {
|
||
|
this->passportId = id;
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
Passport &withCountryId(uint64_t id) {
|
||
|
this->countryId = id;
|
||
|
return *this;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::vector<std::string> getProperties(std::string &line) {
|
||
|
std::vector<std::string> properties;
|
||
|
std::string tmp = line;
|
||
|
size_t end = tmp.find(' ');
|
||
|
|
||
|
while (end != std::variant_npos) {
|
||
|
std::string property = tmp.substr(0, end);
|
||
|
properties.push_back(property);
|
||
|
|
||
|
tmp = tmp.substr(end + 1);
|
||
|
end = tmp.find(' ');
|
||
|
}
|
||
|
|
||
|
properties.push_back(tmp);
|
||
|
|
||
|
return properties;
|
||
|
}
|
||
|
|
||
|
bool isValidHairColour(std::string &str) {
|
||
|
if (str[0] != '#') return false;
|
||
|
return std::variant_npos == str.find_first_not_of("0123456789abcdefABCDEF", 1);
|
||
|
}
|
||
|
|
||
|
bool isValidEyeColour(std::string &str) {
|
||
|
if (str == "amb") return true;
|
||
|
if (str == "blu") return true;
|
||
|
if (str == "brn") return true;
|
||
|
if (str == "gry") return true;
|
||
|
if (str == "grn") return true;
|
||
|
if (str == "hzl") return true;
|
||
|
if (str == "oth") return true;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
int main() {
|
||
|
std::ifstream input{"../input.txt"};
|
||
|
std::vector<Passport> passports;
|
||
|
|
||
|
uint32_t p1ValidCount = 0;
|
||
|
uint32_t p2ValidCount = 0;
|
||
|
std::string line;
|
||
|
Passport p1Passport = {};
|
||
|
Passport p2Passport = {};
|
||
|
while (std::getline(input, line)) {
|
||
|
if (line.empty()) {
|
||
|
if (p1Passport.isValid()) p1ValidCount++;
|
||
|
if (p2Passport.isValid()) p2ValidCount++;
|
||
|
p1Passport = {};
|
||
|
p2Passport = {};
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
std::vector<std::string> properties = getProperties(line);
|
||
|
|
||
|
for (std::string &property : properties) {
|
||
|
std::string type = property.substr(0, 3);
|
||
|
std::string value = property.substr(4);
|
||
|
|
||
|
if (type == "byr") {
|
||
|
try {
|
||
|
uint32_t year = std::stoi(value);
|
||
|
p1Passport.withBirthYear(year);
|
||
|
if (year >= 1920 && year <= 2002) p2Passport.withBirthYear(year);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [BYR]" << std::endl;
|
||
|
}
|
||
|
} else if (type == "iyr") {
|
||
|
try {
|
||
|
uint32_t year = std::stoi(value);
|
||
|
p1Passport.withIssueYear(year);
|
||
|
if (year >= 2010 && year <= 2020) p2Passport.withIssueYear(year);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [IYR]" << std::endl;
|
||
|
}
|
||
|
} else if (type == "eyr") {
|
||
|
try {
|
||
|
uint32_t year = std::stoi(value);
|
||
|
p1Passport.withExpirationYear(year);
|
||
|
if (year >= 2020 && year <= 2030) p2Passport.withExpirationYear(year);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [EYR]" << std::endl;
|
||
|
}
|
||
|
} else if (type == "hgt") {
|
||
|
// Account for whether its inches or centimeters
|
||
|
std::string unit = value.substr(value.length() - 2);
|
||
|
|
||
|
if (unit == "in") {
|
||
|
try {
|
||
|
uint32_t number = std::stoi(value.substr(0, value.length() - 2));
|
||
|
p1Passport.withHeightInInches(number);
|
||
|
if (number >= 59 && number <= 76) p2Passport.withHeightInInches(number);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [HGT:cm]" << std::endl;
|
||
|
}
|
||
|
} else if (unit == "cm") {
|
||
|
try {
|
||
|
uint32_t number = std::stoi(value.substr(0, value.length() - 2));
|
||
|
p1Passport.withHeightInCentimeters(number);
|
||
|
if (number >= 150 && number <= 193) p2Passport.withHeightInCentimeters(number);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [HGT:in]" << std::endl;
|
||
|
}
|
||
|
} else {
|
||
|
// Assume centimeters. NOTE: This case is invalid in Part 2
|
||
|
try {
|
||
|
uint32_t number = std::stoi(value);
|
||
|
p1Passport.withHeightInCentimeters(number);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an integer. [HGT]" << std::endl;
|
||
|
}
|
||
|
}
|
||
|
} else if (type == "hcl") {
|
||
|
p1Passport.withHairColour(value);
|
||
|
if (isValidHairColour(value)) p2Passport.withHairColour(value);
|
||
|
} else if (type == "ecl") {
|
||
|
p1Passport.withEyeColour(value);
|
||
|
if (isValidEyeColour(value)) p2Passport.withEyeColour(value);
|
||
|
} else if (type == "pid") {
|
||
|
try {
|
||
|
uint64_t id = std::stoll(value);
|
||
|
p1Passport.withPassportId(id);
|
||
|
|
||
|
if (value.length() == 9) p2Passport.withPassportId(id);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not a 64-bit integer. [PID]" << std::endl;
|
||
|
p1Passport.withPassportId(0); // NOTE: This is needed to pass part 1.
|
||
|
}
|
||
|
} else if (type == "cid") {
|
||
|
try {
|
||
|
uint64_t id = std::stoll(value);
|
||
|
p1Passport.withCountryId(id);
|
||
|
} catch (const std::exception &) {
|
||
|
std::cout << value << " is not an 64-bit integer. [CID]" << std::endl;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check for last p1Passport and P2Passport
|
||
|
if (p1Passport.isValid()) p1ValidCount++;
|
||
|
if (p2Passport.isValid()) p2ValidCount++;
|
||
|
|
||
|
std::cout << "(P1) Valid Passports: " << p1ValidCount << std::endl;
|
||
|
std::cout << "(P2) Valid Passports: " << p2ValidCount << std::endl;
|
||
|
|
||
|
return 0;
|
||
|
}
|