Compare commits
	
		
			No commits in common. "3d5c13cf6af52da0e30481a4bfadc689151de69f" and "15128fbddf3917761bac488649d633af88841d90" have entirely different histories.
		
	
	
		
			3d5c13cf6a
			...
			15128fbddf
		
	
		
							
								
								
									
										45
									
								
								day4.cr
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								day4.cr
									
									
									
									
									
								
							@ -1,6 +1,20 @@
 | 
			
		||||
require "./passports.cr"
 | 
			
		||||
INPUT = File.read("day4.txt")
 | 
			
		||||
 | 
			
		||||
def parse_passport(string)
 | 
			
		||||
  new_hash = {} of String => String
 | 
			
		||||
  string.split(" ").each do |field|
 | 
			
		||||
    k,v = field.split(":")
 | 
			
		||||
    new_hash[k] = v
 | 
			
		||||
  end
 | 
			
		||||
  new_hash
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def parse_passports(lines)
 | 
			
		||||
  lines.split(/\n\n/).map do |s|
 | 
			
		||||
    parse_passport(s.chomp.gsub(/\n/, " "))
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def part1
 | 
			
		||||
  input = INPUT.clone
 | 
			
		||||
  passports = parse_passports(input)
 | 
			
		||||
@ -12,16 +26,35 @@ def part1
 | 
			
		||||
  puts total
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def validate_range(range)
 | 
			
		||||
  ->(s : String) { s.to_i32?.try { |i| range.includes? i } }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def validate_regex(regex)
 | 
			
		||||
  ->(s : String) { s.matches?(regex) }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def part2
 | 
			
		||||
  input = INPUT.clone
 | 
			
		||||
  passports = parse_passports(input)
 | 
			
		||||
  valid_passports = [] of Passport
 | 
			
		||||
  passports.each do |p|
 | 
			
		||||
    if vp = Passport.from_hash?(p)
 | 
			
		||||
      valid_passports << vp 
 | 
			
		||||
  validators = {
 | 
			
		||||
    "byr" => validate_range(1920..2002),
 | 
			
		||||
    "iyr" => validate_range(2010..2020),
 | 
			
		||||
    "eyr" => validate_range(2020..2030),
 | 
			
		||||
    "hgt" => ->(s : String) {
 | 
			
		||||
      return false unless s.match(/^(\d+)(cm|in)$/)
 | 
			
		||||
      validate_range($~[2] == "cm" ? (150..193) : (59..76)).call($~[1])
 | 
			
		||||
    },
 | 
			
		||||
    "hcl" => validate_regex(/^#[0-9a-f]{6}$/),
 | 
			
		||||
    "ecl" => ->(s : String) { "amb blu brn gry grn hzl oth".split(" ").includes? s },
 | 
			
		||||
    "pid" => ->(s : String) { s =~ /^[0-9]{9}$/ },
 | 
			
		||||
  }
 | 
			
		||||
  total = passports.count do |passport|
 | 
			
		||||
    validators.all? do |k, v|
 | 
			
		||||
      passport[k]?.try { |s| v.call(s) }
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  puts valid_passports.size
 | 
			
		||||
  puts total
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
part1
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										55
									
								
								day5.cr
									
									
									
									
									
								
							
							
						
						
									
										55
									
								
								day5.cr
									
									
									
									
									
								
							@ -1,55 +0,0 @@
 | 
			
		||||
INPUT = File.read("day5.txt").lines.map(&.chomp) 
 | 
			
		||||
 | 
			
		||||
def partition(choices, list)
 | 
			
		||||
  b = 0
 | 
			
		||||
  e = list.size
 | 
			
		||||
  choices.chars.each do |c|
 | 
			
		||||
    b = (b+e)//2 if c == 'B' || c == 'R'
 | 
			
		||||
    e = (b+e)//2 if c == 'F' || c == 'L'
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  return {b,e}
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def get_id(str)
 | 
			
		||||
  col = str[0..6]
 | 
			
		||||
  row = str[7..]
 | 
			
		||||
  {partition(col, (0..127).to_a)[0], partition(row, (0..7).to_a)[0]}
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def id(t)
 | 
			
		||||
  r, c = t
 | 
			
		||||
  r * 8 + c
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def part1
 | 
			
		||||
  input = INPUT.clone
 | 
			
		||||
  max = input.max_of do |line|
 | 
			
		||||
    r, c = get_id(line)
 | 
			
		||||
    r * 8 + c
 | 
			
		||||
  end
 | 
			
		||||
  puts max
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def part2
 | 
			
		||||
  input = INPUT.clone
 | 
			
		||||
  seen = Set(Int32).new
 | 
			
		||||
  max = input.each do |line|
 | 
			
		||||
    seen << id(get_id(line))
 | 
			
		||||
  end
 | 
			
		||||
  candidates = [] of Int32
 | 
			
		||||
  128.times do |r|
 | 
			
		||||
    next if r == 0 || r == 127
 | 
			
		||||
    8.times do |c|
 | 
			
		||||
      next if seen.includes? id({r, c})
 | 
			
		||||
      candidates << id({r,c})
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
  candidates = candidates.select do |i|
 | 
			
		||||
    seen.includes?(i-1) && seen.includes?(i+1)
 | 
			
		||||
  end
 | 
			
		||||
  puts candidates[0]
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
part1
 | 
			
		||||
part2
 | 
			
		||||
							
								
								
									
										48
									
								
								graph.cr
									
									
									
									
									
								
							
							
						
						
									
										48
									
								
								graph.cr
									
									
									
									
									
								
							@ -1,48 +0,0 @@
 | 
			
		||||
class Graph(A)
 | 
			
		||||
  def initialize
 | 
			
		||||
    @edges = {} of A => Set({A, Int32})
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def add_edge(f, t, c = 1)
 | 
			
		||||
    @edges[f] ||= Set({A, Int32}).new
 | 
			
		||||
    @edges[f] << {t, c}
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def add_biedge(f, t, c = 1)
 | 
			
		||||
    add_edge(f, t, c)
 | 
			
		||||
    add_edge(t, f, c)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def find_path(f, t)
 | 
			
		||||
    visited = Set(A).new
 | 
			
		||||
    candidates = Set { f }
 | 
			
		||||
    distances = {f => 0}
 | 
			
		||||
    prev = {} of A => A
 | 
			
		||||
 | 
			
		||||
    while !candidates.empty?
 | 
			
		||||
      candidate = candidates.min_by { |c| distances[c] }
 | 
			
		||||
      break if candidate == t
 | 
			
		||||
      visited << candidate
 | 
			
		||||
      candidates.delete candidate
 | 
			
		||||
      dist = distances[candidate]
 | 
			
		||||
 | 
			
		||||
      @edges.fetch(candidate, Set({A, Int32}).new).each do |e|
 | 
			
		||||
        node, cost = e
 | 
			
		||||
        new_dist = dist + cost
 | 
			
		||||
        candidates << node unless visited.includes? node
 | 
			
		||||
        next if (old_dist = distances[node]?) && old_dist < new_dist
 | 
			
		||||
        distances[node] = new_dist 
 | 
			
		||||
        prev[node] = candidate
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    backtrack = t
 | 
			
		||||
    path = [t] of A
 | 
			
		||||
    while backtrack != f
 | 
			
		||||
      return nil unless prev_bt = prev[backtrack]?
 | 
			
		||||
      path << prev_bt
 | 
			
		||||
      backtrack = prev_bt
 | 
			
		||||
    end
 | 
			
		||||
    {path.reverse!, distances[t]}
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										86
									
								
								passports.cr
									
									
									
									
									
								
							
							
						
						
									
										86
									
								
								passports.cr
									
									
									
									
									
								
							@ -1,86 +0,0 @@
 | 
			
		||||
def parse_passport(string)
 | 
			
		||||
  new_hash = {} of String => String
 | 
			
		||||
  string.split(" ").each do |field|
 | 
			
		||||
    k,v = field.split(":")
 | 
			
		||||
    new_hash[k] = v
 | 
			
		||||
  end
 | 
			
		||||
  new_hash
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def parse_passports(lines)
 | 
			
		||||
  lines.split(/\n\n/).map do |s|
 | 
			
		||||
    parse_passport(s.chomp.gsub(/\n/, " "))
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Validator(T, R)
 | 
			
		||||
  def initialize(@proc : Proc(T, R?))
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def >>(other)
 | 
			
		||||
    Validator.new(->(i : T) { @proc.call(i).try { |j| other.@proc.call(j) } })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def then(&block : R -> S?) forall S
 | 
			
		||||
    Validator(T, S).new(->(i : T) { @proc.call(i).try { |j| block.call(j) } })
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def run(input)
 | 
			
		||||
    @proc.call(input)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Hash(K,V)
 | 
			
		||||
  def validate(k, vl)
 | 
			
		||||
    self[k]?.try { |v| vl.run(v) }
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def range(range) : Validator(Int32, Int32)
 | 
			
		||||
  Validator.new ->(i : Int32) { (range.includes? i) ? i : nil }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def int : Validator(String, Int32)
 | 
			
		||||
  Validator.new ->(s : String) { s.to_i32? }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def regex(regex) : Validator(String, Regex::MatchData)
 | 
			
		||||
  Validator(String, Regex::MatchData).new ->(s : String) { s.match(regex) }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def oneof(strs : Array(A)) : Validator(A, A) forall A
 | 
			
		||||
  Validator(String, String).new ->(s : String) { (strs.includes? s) ? s : nil }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def length(len) : Validator(String, String)
 | 
			
		||||
  Validator(String, String).new ->(s : String) { (s.size == len) ? s : nil }
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def year(rng) : Validator(String, Int32)
 | 
			
		||||
  length(4) >> int >> range(rng)
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
def unit_range(map) : Validator(String, {String, Int32})
 | 
			
		||||
  regex(/(\d+)([a-zA-Z]+)/).then do |md|
 | 
			
		||||
    map[md[2]]?.try { |r| (int >> range(r)).run(md[1]).try { |i| {md[2], i} } }
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
class Passport
 | 
			
		||||
  def initialize(
 | 
			
		||||
    @byr : Int32, @iyr : Int32,
 | 
			
		||||
    @eyr : Int32, @hgt : {String, Int32},
 | 
			
		||||
    @hcl : String, @ecl : String, @pid : Int32)
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def self.from_hash?(hash)
 | 
			
		||||
    return nil unless byr = hash.validate("byr", year(1920..2002))
 | 
			
		||||
    return nil unless iyr = hash.validate("iyr", year(2010..2020))
 | 
			
		||||
    return nil unless eyr = hash.validate("eyr", year(2020..2030))
 | 
			
		||||
    return nil unless hgt = hash.validate("hgt", unit_range({"cm" => (150..193), "in" => (59..76)}))
 | 
			
		||||
    return nil unless hcl = hash.validate("hcl", regex(/^#([0-9a-f]{6})$/).then { |d| d[1] })
 | 
			
		||||
    return nil unless ecl = hash.validate("ecl", oneof(["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]))
 | 
			
		||||
    return nil unless pid = hash.validate("pid", length(9) >> int)
 | 
			
		||||
    Passport.new(byr, iyr, eyr, hgt, hcl, ecl, pid)
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user