1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class Room
  attr_reader :encrypted_name, :sector_id

  def initialize(encrypted_name, sector_id)
    @encrypted_name, @sector_id = encrypted_name, sector_id
  end

  def checksum
    encrypted_name
      .scan(/[a-z]/)
      .group_by {|x| x }
      .map {|k,v| [k, v.size] }
      .sort {|a,b|
        (a.last == b.last) ? a.first <=> b.first : b.last <=> a.last
      }
      .take(5)
      .map(&:first)
      .join
  end

  def name
    name = encrypted_name.chars
    sector_id.times do
      name = name.map {|char|
        case char
        when /[a-y]/
          char.succ
        when ?z
          ?a
        when ?-, ' '
          ' '
        else
          raise "unexpected char: '#{char.inspect}'"
        end
      }
    end
    name.join
  end
end

if __FILE__ == $0
  DATA.read.lines.map {|line|
    /(?<name>.+)-(?<sector_id>\d+)\[(?<checksum>[a-z]+)\]/ =~ line
    room = Room.new(name, sector_id.to_i)
    [room, checksum]
  }.select {|room, checksum|
    room.checksum == checksum && room.name =~ /north/
  }.tap {|rooms|
    p rooms
  }
end

require 'minitest'
# require 'minitest/autorun'

class TestDay04 < Minitest::Test
  def test_day_04
    assert_equal 'abxyz', Room.new('aaaaa-bbb-z-y-x', 123).checksum
    assert_equal 'abcde', Room.new('a-b-c-d-e-f-g-h', 987).checksum
    assert_equal 'oarel', Room.new('not-a-real-room', 404).checksum
    refute_equal 'decoy', Room.new('totally-real-room', 200).checksum

    assert_equal 'very encrypted name', Room.new('qzmt-zixmtkozy-ivhz', 343).name
  end
end

__END__