Skip to content

Instantly share code, notes, and snippets.

@bil-bas
Created October 2, 2012 15:08
Show Gist options
  • Save bil-bas/3819927 to your computer and use it in GitHub Desktop.
Save bil-bas/3819927 to your computer and use it in GitHub Desktop.
Encoding and decoding of strings for KevinSjoberg's problem (https://gist.github.com/3818527)
=begin
Problem Solving Challenge #1 (https://gist.github.com/3818527)
Problem solving challenges are fun. They make use of one's problem solving skills and stimulate the programmer's need to challenge themselves.
Here we go
These 1151159511610110199114 numbers represent a word.
The word may only contain a-zA-Z_
The word is of size 8.
In this particular case the secret word was s_ecrets.
Write two functionsthat can encrypt an arbitrary string and return a string of numbers such as the one above. The program should also be able to take a string of numbers and return the original word.
Happy hacking!
=end
# Perhaps a little over-engineered, but I think a complete and robust solution.
module Encoder
class << self
# Create a mapping of encoded string back to characters { "115" => "s" }
valid_chars = ("A".."Z").to_a + ("a".."z").to_a + ["_"]
VALID_BYTES = Set.new valid_chars.join.bytes
ASCII_TO_CHAR = valid_chars.each_with_object({}) do |c, mapping|
mapping[c.bytes.to_a[0].to_s] = c
end
# Encode a string - validating.
def encode(string)
encoded = string.each_byte.map do |byte|
raise ArgumentError, "Bad character: '#{byte.chr}'" unless VALID_BYTES.include? byte
byte.to_s
end
riffle(encoded).join
end
# Decode string - validating.
def decode(string)
pos = 0 # Current position in string.
decoded = []
# Code is 2 or 3 character strings. Take 2 characters and
# see if it is valid, otherwise take another char and check that.
until pos >= string.length
if (s = string[pos, 2]) and ASCII_TO_CHAR.has_key? s
decoded << ASCII_TO_CHAR[s]
pos += 2
elsif (s << string[pos + 2]) and ASCII_TO_CHAR.has_key? s
decoded << ASCII_TO_CHAR[s]
pos += 3
else
raise ArgumentError, "Bad encoding at string position #{pos}"
end
end
unriffle(decoded).join
end
protected
# Take first and last element from array until array is empty,
# to build a new array.
def riffle(array)
# Split into two halves.
first, second = array.each_slice(array.size / 2).to_a
# Take from the start and end until empty.
first.zip(second.reverse).inject([]) do |memo, (a, b)|
memo.push a, b
end
end
protected
# Reverse the effect of #riffle
def unriffle(array)
first, second = [], []
array.each_slice(2).each do |a, b|
first << a
second.unshift b
end
first + second
end
end
end
# Run with "rspec" rather than "ruby" on the command line to run the specs.
if defined? RSpec
ENCODED = "1151159511610110199114"
STRING = "s_ecrets"
describe Encoder do
describe "encode" do
it "should encode a string" do
Encoder.encode(STRING).should eq ENCODED
end
it "should fail with a bad string" do
->{ Encoder.encode("abcd123") }.should raise_error ArgumentError, "Bad character: '1'"
end
end
describe "decode" do
it "should decode a string" do
Encoder.decode(ENCODED).should eq STRING
end
it "should fail with a bad encoded string" do
->{ Encoder.decode("11599123455") }.should raise_error ArgumentError, "Bad encoding at string position 5"
end
end
describe "riffle" do
it "should riffle an array" do
Encoder.send(:riffle, [1, 3, 4, 2]).should eq [1, 2, 3, 4]
end
end
describe "unriffle" do
it "should unriffle an array" do
Encoder.send(:unriffle, [1, 2, 3, 4]).should eq [1, 3, 4, 2]
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment