For the first part, my approach was to generate all valid strings and count the messages that appeared there. This runs in a few seconds since the number of valid strings is relatively small.
Part 2 was really scary, but once I noticed that the modified rules were at the end of the chain (near rule 0), it was easier. Did the first approach to get the matching messages, then did some simplification of the rules + regex matching on each remaining message to find valid ones.
require'set'# We'll evaluate the requested rule and generate all valid matches# Then check if each message is within the listdefparse_rule(rule)parsed={}if(match=rule.match(/^"(?<char>\w)"$/))parsed[:match]=match[:char]elsifrule.include?("|")parsed[:or]=rule.split("|").map(&:split)elseparsed[:and]=rule.splitendparsedenddefgenerate_matches(rules,rule_number)rule_to_evaluate=rules[rule_number]parsed=parse_rule(rule_to_evaluate)ifparsed[:match]return[parsed[:match]]elsifparsed[:and]matches=[]parsed[:and].eachdo|dependent_rule|dependent_matches=generate_matches(rules,dependent_rule)ifmatches.empty?matches=dependent_matcheselsegen=[]matches.eachdo|match|dependent_matches.eachdo|dm|gen<<(match+dm)endendmatches=genendendmatcheselsifparsed[:or]matches=parsed[:or].flat_mapdo|dependent_ruleset|sub_matches=[]dependent_ruleset.eachdo|dependent_rule|dependent_matches=generate_matches(rules,dependent_rule)ifsub_matches.empty?sub_matches=dependent_matcheselsegen=[]sub_matches.eachdo|match|dependent_matches.eachdo|dm|gen<<(match+dm)endendsub_matches=genendendsub_matchesendendendrules,messages=File.read("input.txt").split("\n\n")rules=rules.lines.map{|line|line.chomp.split(": ")}.to_hmatches=Set.newgenerate_matches(rules,"0")messages=messages.split("\n")matching_messages=messages.select{|message|matches.include?(message)}original_matching=matching_messages.countmessages-=matching_messages# Changing:# Rule "8" = "42 | 42 8"# Rule "11" = "42 31 | 42 11 31"# Rule 8 translates to X, or XX, or XXX, or XXXX... = itself n times, where X is any match(42)# Rule 11 translates to XY or XXYY or XXXYYY...= X k times, Y k times, where X is any match(42) and Y is any match(31)matches_42=generate_matches(rules,"42")length_of_42_match=matches_42[0].size# All 42-matches have the same length (by inspection)matches_42=Set.new(matches_42)matches_31=generate_matches(rules,"31")length_of_31_match=matches_31[0].size# All 31-matches have the same length (by inspection)matches_31=Set.new(matches_31)# Then Rule 0 is "8 11", which means both must match.# XnWkYk where X and W are matches of 42 (they don't have to be the same match)rule_42_regex="((\\w{#{length_of_42_match}}){2,})"# X must appear at least twicerule_31_regex="(\\w{#{length_of_31_match}})+"rule_0_regex=Regexp.new("^#{rule_42_regex}#{rule_31_regex}$")matching=messages.selectdo|message|is_valid=falseifmessage.match(rule_0_regex)all_matches=message.scan(Regexp.new(".{#{length_of_42_match}}"))checking_31=falsematches_for_42=[]matches_for_31=[]has_matches=all_matches.each_with_indexdo|match,index|# Last item must always be checked against 31ifindex==all_matches.size-1if(matches_31.include?(match))matches_for_31<<matchbreaktrueelsebreakfalseendendifchecking_31ifmatches_31.include?(match)matches_for_31<<matchelsebreakfalseendelse# We're checking 42ifmatches_42.include?(match)matches_for_42<<matchelse# Once we reach the first non-42, we switch to checking for 31ifmatches_31.include?(match)matches_for_31<<matchchecking_31=trueelsebreakfalseendendendendifhas_matches==trueis_valid=matches_for_42.size>matches_for_31.sizeelseis_valid=falseendendis_valid==false?false:trueendporiginal_matchingporiginal_matching+matching.size
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.
My Ruby solution.
For the first part, my approach was to generate all valid strings and count the messages that appeared there. This runs in a few seconds since the number of valid strings is relatively small.
Part 2 was really scary, but once I noticed that the modified rules were at the end of the chain (near rule 0), it was easier. Did the first approach to get the matching messages, then did some simplification of the rules + regex matching on each remaining message to find valid ones.