class EightCorner::StringMapper

StringMapper provides various methods for converting strings to percentage/potential values, which can then be mapped to (x,y) points.

Public Class Methods

new(options={}) click to toggle source
# File lib/eight_corner/string_mapper.rb, line 7
def initialize(options={})
  defaults = {
    group_count: 7,
    min_group_size: 3
  }
  Base.validate_options!(options, defaults)
  options = defaults.merge(options)

  @group_count = options[:group_count]
  @group_max_idx = @group_count - 1
  @min_group_size = options[:min_group_size]

  @frequencies = {"E"=>0.103202, "A"=>0.095238, "R"=>0.092638, "N"=>0.087925, "O"=>0.075898, "S"=>0.065659, "L"=>0.064196, "I"=>0.046481, "T"=>0.040793, "H"=>0.03868, "C"=>0.038193, "D"=>0.03413, "M"=>0.033317, "B"=>0.023891, "G"=>0.023566, "Y"=>0.022103, "U"=>0.021615, "W"=>0.019178, "K"=>0.016252, "P"=>0.015602, "F"=>0.011539, "V"=>0.010889, "Z"=>0.010076, "J"=>0.003738, " "=>0.00195, "X"=>0.00195, "Q"=>0.000975}
end

Public Instance Methods

group1(str, idx) click to toggle source

sequential series of characters extracted from string. loops back to beginning for short strings

# File lib/eight_corner/string_mapper.rb, line 54
def group1(str, idx)
  range = 0..@group_max_idx
  return ArgumentError, "argument must be in #{range}" if ! range.include?(idx)

  str_size = str.size
  g_size = group_size str

  out = ""

  start_idx = (idx * g_size)
  end_idx = start_idx + g_size

  (start_idx...end_idx).each do |x|
    out += str[x % str_size]
  end

  out
end
group2(str, idx) click to toggle source

builds a group from every nth character in the string.

# File lib/eight_corner/string_mapper.rb, line 74
def group2(str, idx)
  str_size = str.size

  out = ''
  group_size(str).times do |i|
    out += str[(i * @group_count + idx) % str_size]
  end
  out
end
group_size(str) click to toggle source

how many characters should be in each group?

# File lib/eight_corner/string_mapper.rb, line 85
def group_size(str)
  [
    str.size / @group_count,
    @min_group_size
  ].max
end
groups(str, method) click to toggle source

split a string into groups, via :method

# File lib/eight_corner/string_mapper.rb, line 42
def groups(str, method)
  raise ArgumentError, "Invalid method #{arg}" if ! respond_to?(method)

  out = []
  @group_count.times do |i|
    out << send(method, str, i)
  end
  out
end
percentize_frequency(str) click to toggle source

turn a string into a float 0..1 a string with common letters should be near 0.5. a string with uncommon letters should be near 0 or 1.

# File lib/eight_corner/string_mapper.rb, line 104
def percentize_frequency(str)
  # letters aren't evenly distributed.
  # a string that has every letter once would add up to 1.

  # totally common: sum would be E*3

  common = @frequencies.first[1] * str.size

  sum = 0
  str.upcase.each_char {|c| sum += @frequencies[c].to_f }

  distance = common - sum # distance from common.

  # add or subtract from 0.5?
  # all letters are positive/negative based on order in frequency distribution.
  m = 1
  @frequencies.keys.each do |i|
    m *= -1
    break if str[0].upcase == i
  end

  interp = Interpolate::Points.new({0=>0, common=>0.5})
  pct_distance = interp.at(distance)
  # interpolate (0 .. common) => (0 .. 0.5)
  # multiply by m and add to 0.5

  pct_distance * m + 0.5
end
percentize_modulus(str) click to toggle source
# File lib/eight_corner/string_mapper.rb, line 92
def percentize_modulus(str)
  # i+memo just to add some order-dependency. "alex" != "xela"
  (str.each_byte.inject(0){|memo,i| memo += i + memo; memo} % 100)/100.to_f
end
percentize_modulus_exp(str) click to toggle source
# File lib/eight_corner/string_mapper.rb, line 97
def percentize_modulus_exp(str)
  (str.each_byte.inject(0){|memo,i| memo += i^2 + memo; memo} % 100)/100.to_f
end
potentials(groups, percentizeA, percentizeB) click to toggle source

return an array of 2-float arrays. provide method symbols for and percentizing method for constructing the 2 array elements.

# File lib/eight_corner/string_mapper.rb, line 24
def potentials(groups, percentizeA, percentizeB)
  [percentizeA, percentizeB].each do |arg|
    raise ArgumentError, "Invalid method #{arg}" if ! respond_to?(arg)
  end

  out = []
  groups.each do |i|
    # puts send(groupA, str, i)
    out << [
      send(percentizeA, i),
      send(percentizeB, i),
    ]
  end

  out
end