Practice coding faster

map_with_index.  Why is there no map_with_index in ruby?  Ends up it's because you don't need it.  You can simply do this:

1
2
3
4
["Two", "Birds", "In", "Hand"].each_with_index.map { |element, index|
  "#{element}: #{index}"
}
# => ["Two: 0", "Birds: 1", "In: 2", "Hand: 3"]

Even now, I'm learning things about Ruby.  The rabbit hole is deep.

Anyway, a post about learning how to code fast came across my desk.  I knew that I wasn't quite as fast as other coders, but I had always thought that I thought deeper on the solution.  But I think what he says makes sense.  I know that violinists slowly ramp up their speed to a point where they're almost making mistakes, faster than they'd actually play the piece to practice playing it at the correct speed.  Same with drawing.  The more you practice drawing faster, the better you get at being economic with your strokes.  So I figured I'd try the same with programming, since I've never done much of this type of exercise.

I decided to do the first Ruby Quiz, since I was most familiar with Ruby, and I should be able to do it quickly.  It took me about three hours, including reading the instructions, going on bathroom breaks, etc.  I think I should have been faster, and I noticed where I slowed down.  I found myself trying things out in irb a lot because I didn't know the exact behavior of some array and string functions.  Also, I spent some time in the beginning pondering how to structure it–should it be a class, or just a collection of functions, or should I extend the classes?  

I'm not thrilled about how it's structured, but it works.  Well, there's a small bug in there, but I'm going to refrain from fixing it.  It'll tack on another 30 mins.  The point of the exercise is that I can see what I need to work on.  I'll try again next time with the next ruby quiz.

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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
#!/usr/bin/ruby

class Encrypter
  private
  def value(symbol)
    return 53 if symbol == "A" or symbol == "B"
    return symbol.to_i
  end

  def triple_cut
    if @deck.index("A") > @deck.index("B")
      top_index = @deck.index("A")
      bottom_index = @deck.index("B")
    else
      top_index = @deck.index("B")
      bottom_index = @deck.index("A")
    end
    bottom = @deck[0...bottom_index]
    middle = @deck[bottom_index..top_index]
    top = @deck[(top_index) + 1..-1]
    @deck = top + middle + bottom
  end

  def count_cut
    count = value(@deck.first)
    @deck = [@deck[0]] + @deck[-count..-1] + @deck[1...-count]
  end

  public
  def initialize
    @deck = ["B", "A"] + (1..52).to_a.reverse
    # key the deck

  end

  def next_key
    jokerA_index = @deck.index("A")
    @deck.move(jokerA_index, jokerA_index - 1)
    jokerB_index = @deck.index("B")
    @deck.move(jokerB_index, jokerB_index - 2)
    @deck = triple_cut
    @deck = count_cut
    top_count = value(@deck.last)
    return [@deck[-(top_count + 1)]].to_alpha
  end
end

class String
  def keystream
    enc = Encrypter.new
    self.gsub(/[^A-Za-z]/, "").split("").map { |char|
      enc.next_key
    }.join
  end
  
  def segment_by(number)
    segments = []
    self.gsub(/[^A-Za-z]/, "").upcase.split("").each_slice(number) do |slice|
      segments << slice.join
    end
    (number - segments.last.length).times { segments.last << "X" } if segments.last.length < number
    return segments
  end

  def to_numeric
    self.split("").map { |char| char[0] - 64 }
  end

end

class Array
  def to_alpha
    self.map { |number| (number.to_i % 26 + 64).chr }.join
  end

  def move(from, to)
    element = self.delete_at(from)
    to -= 1 if (to < 0)
    self.insert(to, element)
  end
end

def merge_numerics(message, keystream)
  message.each_with_index.map do |segment, segment_index|
    segment.each_with_index.map do |message_number, char_index|
      keystream_number = keystream[segment_index][char_index]
      yield message_number, keystream_number
    end
  end
end

def codec(command, message)
  message_numerics = message.segment_by(5).map { |s| s.to_numeric }
  keystream_numerics = message.keystream.segment_by(5).map { |s| s.to_numeric }
  merge_numerics(message_numerics, keystream_numerics) { |m, k|
    case command
    when "encode"
      (m + k) % 26
    when "decode"
      (m - k) % 26
    end
  }.map { |segment|
    segment.to_alpha
  }
end

puts "Welcome to the Solitaire Cipher"
puts

# get the input
command = ARGV.shift
message = ARGV.shift
puts "You entered: #{message}"
puts "Output: #{codec(command, message).join.inspect}"
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s