Module: Sashite::Cell

Defined in:
lib/sashite/cell.rb

Overview

CELL (Coordinate Encoding for Layered Locations) implementation for Ruby

Provides functionality for working with multi-dimensional game board coordinates using a cyclical ASCII character system.

This implementation is strictly compliant with CELL Specification v1.0.0

Constant Summary collapse

REGEX =

Regular expression for validating CELL coordinates according to specification v1.0.0 Optimized version with redundant nested repeat operator removed for clean Ruby execution

/\A[a-z]+(?:[1-9]\d*[A-Z]+[a-z]+)*(?:[1-9]\d*[A-Z]*)?\z/

Class Method Summary collapse

Class Method Details

.component_to_index(component, type) ⇒ Integer

Convert a component to its 0-indexed position

Parameters:

  • component (String)

    the component

  • type (Symbol)

    the component type

Returns:

  • (Integer)

    the 0-indexed position



172
173
174
175
176
177
178
179
180
181
# File 'lib/sashite/cell.rb', line 172

def self.component_to_index(component, type)
  case type
  when :lowercase
    letters_to_index(component)
  when :numeric
    component.to_i - 1
  when :uppercase
    letters_to_index(component.downcase)
  end
end

.dimension_type(dimension) ⇒ Symbol

Determine the character set type for a given dimension Following CELL specification cyclical system: dimension n % 3 determines character set

Parameters:

  • dimension (Integer)

    the dimension number (1-indexed)

Returns:

  • (Symbol)

    :lowercase, :numeric, or :uppercase



136
137
138
139
140
141
142
# File 'lib/sashite/cell.rb', line 136

def self.dimension_type(dimension)
  case dimension % 3
  when 1 then :lowercase  # n % 3 = 1: Latin lowercase letters
  when 2 then :numeric    # n % 3 = 2: Arabic numerals
  when 0 then :uppercase  # n % 3 = 0: Latin uppercase letters
  end
end

.dimensions(string) ⇒ Integer

Get the number of dimensions in a coordinate

Examples:

Sashite::Cell.dimensions("a1")     # => 2
Sashite::Cell.dimensions("a1A")    # => 3
Sashite::Cell.dimensions("foobar") # => 1

Parameters:

  • string (String)

    the coordinate string

Returns:

  • (Integer)

    the number of dimensions



43
44
45
46
47
# File 'lib/sashite/cell.rb', line 43

def self.dimensions(string)
  return 0 unless valid?(string)

  parse(string).length
end

.extract_component(string, type) ⇒ String?

Extract the next component from a string based on expected type Strictly follows CELL specification patterns

Parameters:

  • string (String)

    the string to extract from

  • type (Symbol)

    the expected component type

Returns:

  • (String, nil)

    the extracted component or nil if invalid



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/sashite/cell.rb', line 150

def self.extract_component(string, type)
  case type
  when :lowercase
    # Latin lowercase letters: [a-z]+
    match = string.match(/\A([a-z]+)/)
    match ? match[1] : nil
  when :numeric
    # Arabic numerals: [1-9]\d* (CELL specification requires positive integers only)
    match = string.match(/\A([1-9]\d*)/)
    match ? match[1] : nil
  when :uppercase
    # Latin uppercase letters: [A-Z]+
    match = string.match(/\A([A-Z]+)/)
    match ? match[1] : nil
  end
end

.from_indices(*indices) ⇒ String

Convert an array of 0-indexed integers to a CELL coordinate

Examples:

Sashite::Cell.from_indices(0, 0)     # => "a1"
Sashite::Cell.from_indices(4, 3)     # => "e4"
Sashite::Cell.from_indices(0, 0, 0)  # => "a1A"

Parameters:

  • indices (Array<Integer>)

    splat arguments of 0-indexed positions

Returns:

  • (String)

    the CELL coordinate



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/sashite/cell.rb', line 93

def self.from_indices(*indices)
  return "" if indices.empty?

  result = indices.map.with_index do |index, dimension|
    dimension_type = dimension_type(dimension + 1)
    index_to_component(index, dimension_type)
  end.join

  # Verify the result is valid according to CELL specification
  valid?(result) ? result : ""
end

.index_to_component(index, type) ⇒ String

Convert a 0-indexed position to a component

Parameters:

  • index (Integer)

    the 0-indexed position

  • type (Symbol)

    the component type

Returns:

  • (String)

    the component



188
189
190
191
192
193
194
195
196
197
# File 'lib/sashite/cell.rb', line 188

def self.index_to_component(index, type)
  case type
  when :lowercase
    index_to_letters(index)
  when :numeric
    (index + 1).to_s
  when :uppercase
    index_to_letters(index).upcase
  end
end

.index_to_letters(index) ⇒ String

Convert 0-indexed position to letter sequence Extended alphabet per CELL specification: 0=a, 1=b, …, 25=z, 26=aa, 27=ab, …, 701=zz, 702=aaa, etc.

Parameters:

  • index (Integer)

    the 0-indexed position

Returns:

  • (String)

    the letter sequence



226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/sashite/cell.rb', line 226

def self.index_to_letters(index)
  # Find the length of the result
  length = 1
  base = 0

  loop do
    range_size = 26**length
    break if index < base + range_size

    base += range_size
    length += 1
  end

  # Convert within the found length
  adjusted_index = index - base
  result = ""

  length.times do |pos|
    char_index = adjusted_index / (26**(length - pos - 1))
    result += (char_index + 97).chr
    adjusted_index %= (26**(length - pos - 1))
  end

  result
end

.letters_to_index(letters) ⇒ Integer

Convert letter sequence to 0-indexed position Extended alphabet per CELL specification: a=0, b=1, …, z=25, aa=26, ab=27, …, zz=701, aaa=702, etc.

Parameters:

  • letters (String)

    the letter sequence

Returns:

  • (Integer)

    the 0-indexed position



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/sashite/cell.rb', line 204

def self.letters_to_index(letters)
  length = letters.length
  index = 0

  # Add positions from shorter sequences
  (1...length).each do |len|
    index += 26**len
  end

  # Add position within current length
  letters.each_char.with_index do |char, pos|
    index += (char.ord - 97) * (26**(length - pos - 1))
  end

  index
end

.parse(string) ⇒ Array<String>

Parse a coordinate string into dimensional components

Examples:

Sashite::Cell.parse("a1A")      # => ["a", "1", "A"]
Sashite::Cell.parse("h8Hh8")    # => ["h", "8", "H", "h", "8"]
Sashite::Cell.parse("foobar")   # => ["foobar"] (if valid single dimension)

Parameters:

  • string (String)

    the coordinate string to parse

Returns:

  • (Array<String>)

    array of dimensional components



58
59
60
61
62
63
64
# File 'lib/sashite/cell.rb', line 58

def self.parse(string)
  return [] unless string.is_a?(::String)
  return [] if string.empty?
  return [] unless valid?(string)

  parse_recursive(string, 1)
end

.parse_recursive(string, dimension) ⇒ Array<String>

Recursively parse a coordinate string into components following the strict CELL specification cyclical pattern

Parameters:

  • string (String)

    the remaining string to parse

  • dimension (Integer)

    the current dimension (1-indexed)

Returns:

  • (Array<String>)

    array of dimensional components



118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/sashite/cell.rb', line 118

def self.parse_recursive(string, dimension)
  return [] if string.empty?

  expected_type = dimension_type(dimension)
  component = extract_component(string, expected_type)

  return [] if component.nil?

  # Extract component and recursively parse the rest
  remaining = string[component.length..]
  [component] + parse_recursive(remaining, dimension + 1)
end

.regexRegexp

Get the validation regular expression

Returns:

  • (Regexp)

    the CELL validation regex from specification v1.0.0



108
109
110
# File 'lib/sashite/cell.rb', line 108

def self.regex
  REGEX
end

.to_indices(string) ⇒ Array<Integer>

Convert a CELL coordinate to an array of 0-indexed integers

Examples:

Sashite::Cell.to_indices("a1")   # => [0, 0]
Sashite::Cell.to_indices("e4")   # => [4, 3]
Sashite::Cell.to_indices("a1A")  # => [0, 0, 0]

Parameters:

  • string (String)

    the CELL coordinate

Returns:

  • (Array<Integer>)

    array of 0-indexed positions



75
76
77
78
79
80
81
82
# File 'lib/sashite/cell.rb', line 75

def self.to_indices(string)
  return [] unless valid?(string)

  parse(string).map.with_index do |component, index|
    dimension_type = dimension_type(index + 1)
    component_to_index(component, dimension_type)
  end
end

.valid?(string) ⇒ Boolean

Check if a string represents a valid CELL coordinate

Examples:

Sashite::Cell.valid?("a1")     # => true
Sashite::Cell.valid?("a1A")    # => true
Sashite::Cell.valid?("*")      # => false
Sashite::Cell.valid?("a0")     # => false

Parameters:

  • string (String)

    the string to validate

Returns:

  • (Boolean)

    true if the string is a valid CELL coordinate



26
27
28
29
30
31
32
# File 'lib/sashite/cell.rb', line 26

def self.valid?(string)
  return false unless string.is_a?(String)
  return false if string.empty?

  # Use the optimized CELL v1.0.0 regex for validation
  string.match?(REGEX)
end