Class: Fable::InkList

Inherits:
Object
  • Object
show all
Defined in:
lib/fable/ink_list.rb

Overview

The InkList is the underlying type that’s used to store an instance of a list in ink. It’s not used for the definition of the list, but for a list value that’s stored in a variable.

Defined Under Namespace

Classes: InkListItem

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeInkList

Create a new empty ink list



59
60
61
62
# File 'lib/fable/ink_list.rb', line 59

def initialize
  self.list = {}
  self.origins = []
end

Instance Attribute Details

#listObject

Returns the value of attribute list.



56
57
58
# File 'lib/fable/ink_list.rb', line 56

def list
  @list
end

#originsObject

Returns the value of attribute origins.



56
57
58
# File 'lib/fable/ink_list.rb', line 56

def origins
  @origins
end

Class Method Details

.copy_list(other_list) ⇒ Object



64
65
66
67
68
69
70
71
72
# File 'lib/fable/ink_list.rb', line 64

def self.copy_list(other_list)
  ink_list = self.new
  other_list_data = other_list.list.map{|item, int_value| [InkList::InkListItem.new(full_name: item.full_name), int_value] }
  ink_list.list = Hash[other_list_data]
  ink_list.origins = other_list.origins
  ink_list.set_initial_origin_names(other_list.origin_names)

  return ink_list
end

.from_string(my_list_item, origin_story) ⇒ Object

Converts a string to an ink list, and returns for use in the Story



99
100
101
102
103
104
105
106
107
108
109
# File 'lib/fable/ink_list.rb', line 99

def self.from_string(my_list_item, origin_story)
  list_value = origin_story.list_definitions[my_list_item]
  if list_value.nil?
    raise Error("Could not find the InkListItem from the string '#{my_list_item}' to create an InkList because it doesn't exist in the original list definition in ink.")
  else
    ink_list = self.new
    ink_list.list = list_value
  end

  return ink_list
end

.new_for_origin_definition_and_story(single_origin_list_name, origin_story) ⇒ Object

Create a new empty ink list that’s intended to hold items from a particular origin list definition. The origin story is needed in order to be able to look up that definition



84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/fable/ink_list.rb', line 84

def self.new_for_origin_definition_and_story(single_origin_list_name, origin_story)
  ink_list = self.new
  ink_list.set_initial_origin_name(single_origin_list_name)

  list_definition = origin_story.list_definitions.find_list(single_origin_list_name)
  if list_definition.nil?
    raise Error("InkList origin could not be found in story when constructing new list: #{single_origin_list_name}")
  else
    ink_list.origins = [list_definition]
  end

  return ink_list
end

.new_with_single_item(item, value) ⇒ Object



74
75
76
77
78
79
# File 'lib/fable/ink_list.rb', line 74

def self.new_with_single_item(item, value)
  ink_list = self.new
  ink_list.list[item] = value

  return ink_list
end

Instance Method Details

#&(other_list) ⇒ Object

Return a new list that is the intersection of the current list and one that’s passed in. The equivalent of calling (list1 ^ list2) in ink.



269
270
271
272
273
274
275
276
277
278
# File 'lib/fable/ink_list.rb', line 269

def &(other_list)
  intersection_list = self.class.new
  self.list.each do |item, int_value|
    if other_list.list.any?{|other_item, other_value| other_item.equal?(item)}
      intersection_list.list[item] = int_value
    end
  end

  return intersection_list
end

#+(other_list) ⇒ Object

Return a new list that is a combination of the current list and one that’s passed in. The equivalent of calling (list1 + list2) in ink.



258
259
260
261
262
263
264
265
# File 'lib/fable/ink_list.rb', line 258

def +(other_list)
  union_list = self.class.copy_list(self)
  other_list.list.each do |item, int_value|
    union_list.list[item] = int_value
  end

  return union_list
end

#-(other_list) ⇒ Object

Returns a new list that’s the same as the current one, except with the given items removed that are in the passed-in list. Equivalent to calling (list1 - list2) in ink.



283
284
285
286
287
288
289
290
# File 'lib/fable/ink_list.rb', line 283

def -(other_list)
  without_list = self.class.copy_list(self)
  other_list.list.each do |item, int_value|
    without_list.list.delete_if{ |without_list_item, other_value| without_list_item.equal?(item)}
  end

  return without_list
end

#<(other_list) ⇒ Object

Returns true if all the item values in the current list are less than all the item values in the passed-in list. Equivalent to calling (list1 < list2 in ink)



325
326
327
328
329
330
# File 'lib/fable/ink_list.rb', line 325

def <(other_list)
  return false if other_list.list.empty?
  return true if self.list.empty?

  return self.max_item[1] < other_list.min_item[1]
end

#<=(other_list) ⇒ Object

Returns true if the item values in the current list overlap, or are all less than the item values in the passed in list. None of the item values in the current list must go above the item values in the passed in list. Equivalent to (list1 <= list2) in ink, or LIST_MAX(list1) <= LIST_MAX(list2) && LIST_MIN(list1) <= LIST_MIN(list2)



336
337
338
339
340
341
342
343
344
# File 'lib/fable/ink_list.rb', line 336

def <=(other_list)
  return false if other_list.list.empty?
  return true if self.list.empty?

  return (
    self.max_item[1] <= other_list.max_item[1] &&
    self.min_item[1] <= other_list.min_item[1]
  )
end

#==(other_list) ⇒ Object

Returns true if the passed object is also an ink list that contains the same items as the current list, false otherwise.



400
401
402
403
404
405
# File 'lib/fable/ink_list.rb', line 400

def ==(other_list)
  return false if !other_list.is_a?(InkList)
  return false if other_list.list.size != self.list.size

  return self.list.all?{|item, int_value| other_list.list.has_key?(item) }
end

#>(other_list) ⇒ Object

Returns true if all the item values in the current list are greater than all the item values in the passed-in list. Equivalent to calling (list1 > list2) in ink.



301
302
303
304
305
306
# File 'lib/fable/ink_list.rb', line 301

def >(other_list)
  return false if self.list.empty?
  return true if other_list.list.empty?

  return self.min_item[1] > other_list.max_item[1]
end

#>=(other_list) ⇒ Object

Returns true if the item values in the current list overlap, or are all greater than the item values in the passed-in list. None of the item values in the current list must fall below the item values in the passed-in list Equivalent to (list1 >= list2) in ink, or LIST_MIN(list1) >= LIST_MIN(list2) && LIST_MAX(list1) >= LIST_MAX(list2)



313
314
315
316
317
318
319
320
321
# File 'lib/fable/ink_list.rb', line 313

def >=(other_list)
  return false if self.list.empty?
  return true if other_list.list.empty?

  return (
    self.min_item[1] >= other_list.min_item[1] &&
    self.max_item[1] >= other_list.max_item[1]
  )
end

#add_ink_list_item(item) ⇒ Object

Adds the given item to the ink list. Note that the item must come from a list definition that is already “known” to this list, so that teh item’s value can be looked up. By “known”, we mean that it alreadyu has items in it from that source, or did at one point. It can’t be a completely fresh empty list, or a list that only contains items from a different list definition



128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/fable/ink_list.rb', line 128

def add_ink_list_item(item)
  if item.origin_name.nil?
    return add_item_from_string(item.item_name)
  end

  origins.each do |origin|
    if origin.name == item.origin_name
      integer_value = origin.items[item]

      if integer_value.nil?
        raise Error("Could not add the item '#{item.item_name}' to this list because it doesn't exist in the original list definition in ink.")
      else
        self.list[item] = integer_value
      end
    end
  end

  raise Error("Failed to add item to list because the item was from a new list definition that wasn't previously known to this list. Only items from previously known lists can be used, so that the int value can be found.")
end

#add_item(item_or_item_name) ⇒ Object



115
116
117
118
119
120
121
# File 'lib/fable/ink_list.rb', line 115

def add_item(item_or_item_name)
  if item_or_item_name.is_a?(InkListItem)
    add_ink_list_item(item_or_item_name)
  else
    add_item_from_string(item_or_item_name)
  end
end

#add_item_from_string(item_name) ⇒ Object

Adds the given item to the ink list, attempting to find the origin list definition that it belongs to. The item must therefore come from a list definition that is already “known” to this list, so that the item’s value can be looked up. By “known”, we mean that it already has items in it from that source, or it did at one point. It can’t be a completely fresh empty list, or a list that only contains items from a different list definition



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/fable/ink_list.rb', line 153

def add_item_from_string(item_name)
  found_list_definition = nil
  origins.each do |origin|
    if origin.items.any?{|item, int_value| item.name == item_name }
      if found_list_definition.nil?
        found_list_definition = origin
      else
        raise Error("Could not add the item '#{item_name}' to this list because it could come from either '#{origin.name}' or '#{found_list_definition.name}'")
      end
    end
  end

  if found_list_definition.nil?
    raise Error("Could not add the item '#{item_name}' to this list because it isn't known to any list definitions previously associated with this list.")
  end

  item = InkListItem.new(origin_name: found_list_definition.name, item_name: item_name)
  item_value = found_list_definition.value_for_item(item)
  self.items[item] = item_value
end

#allObject

The list of all items from the original list definition, equivalent to calling LIST_ALL(list) in ink.



245
246
247
248
249
250
251
252
253
254
# File 'lib/fable/ink_list.rb', line 245

def all
  new_list = self.class.new
  origins.each do |origin|
    origin.items.each do |item, int_value|
      new_list.list[item] = int_value
    end
  end

  new_list
end

#contains?(other_list) ⇒ Boolean

Returns true if the current list contains all the items that are in the list that is passed in. Equivalent to calling (list1 ? list2) in ink.

Returns:

  • (Boolean)


294
295
296
# File 'lib/fable/ink_list.rb', line 294

def contains?(other_list)
  other_list.list.all?{|other_item, other_value| self.list.any?{|item, value| other_item.equal?(item) }}
end

#countObject



207
208
209
# File 'lib/fable/ink_list.rb', line 207

def count
  list.size
end

#include_item_named?(item_name) ⇒ Boolean

Returns:

  • (Boolean)


174
175
176
# File 'lib/fable/ink_list.rb', line 174

def include_item_named?(item_name)
  list.any?{|item, int_value|  item.name == item_name}
end

#inverseObject

The inverse of the list, equivalent to colling LIST_INVERSE(list) in ink.



230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/fable/ink_list.rb', line 230

def inverse
  new_list = self.class.new
  origins.each do |origin|
    origin.items.each do |item, int_value|
      if self.list.none?{|item_to_search, other_value| item_to_search.equal?(item)}
        new_list.list[item] = int_value
      end
    end
  end

  new_list
end

#item_with_value(value) ⇒ Object



111
112
113
# File 'lib/fable/ink_list.rb', line 111

def item_with_value(value)
  list.key(value)
end

#list_with_subrange(min_bound, max_bound) ⇒ Object

Returns a sublist with the elements given in the minimum & maximum bounds. The bounds can either be ints, which are indicies into the entire (sorted) list, or they can be InkLists themsevles. These are intended to be single-item lists, so you can specify the upper & lower bounds. If you pass in multi-item lists, it’ll use the minimum and maximum items in those lists, respectively.



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
# File 'lib/fable/ink_list.rb', line 367

def list_with_subrange(min_bound, max_bound)
  return self.class.new if self.list.empty?

  ordered = self.ordered_items

  min_value = 0
  max_value = Float::INFINITY

  if min_bound.is_a?(Numeric)
    min_value = min_bound
  elsif min_bound.is_a?(InkList) && !min_bound.list.empty?
    min_value = min_bound.min_item[1]
  end

  if max_bound.is_a?(Numeric)
    max_value = max_bound
  elsif max_bound.is_a?(InkList) && !max_bound.list.empty?
    max_value = max_bound.max_item[1]
  end

  sublist = self.class.new
  sublist.set_initial_origin_names(origin_names)
  ordered.each do |item, int_value|
    if int_value >= min_value && int_value <= max_value
      sublist.list[item] = int_value
    end
  end

  sublist
end

#max_as_listObject



346
347
348
349
350
351
352
# File 'lib/fable/ink_list.rb', line 346

def max_as_list
  if self.list.empty?
    return self.class.new
  else
    return self.class.new_with_single_item(max_item)
  end
end

#max_itemObject

Get the maximum item in the list, equivalent to calling LIST_MAX(list) in ink.



212
213
214
215
216
217
218
# File 'lib/fable/ink_list.rb', line 212

def max_item
  list.max do |a, b|
    return -1 if a[0].null_item?
    return 1 if b[0].null_item?
    a[1] <=> b[1]
  end
end

#min_as_listObject



354
355
356
357
358
359
360
# File 'lib/fable/ink_list.rb', line 354

def min_as_list
  if self.list.empty?
    return self.class.new
  else
    return self.class.new_with_single_item(min_item)
  end
end

#min_itemObject

Get the minimum item in the list, equivalent to calling LIST_MIN(list) in ink.



221
222
223
224
225
226
227
# File 'lib/fable/ink_list.rb', line 221

def min_item
  list.min do |a, b|
    return -1 if a[0].null_item?
    return 1 if b[0].null_item?
    a[1] <=> b[1]
  end
end

#ordered_itemsObject



407
408
409
410
411
412
413
414
415
416
# File 'lib/fable/ink_list.rb', line 407

def ordered_items
  self.list.sort do |a, b|
    # ensure consistent ordering of mixed lists
    if(a[1] == b[1])
      a[0].origin_name <=> b[0].origin_name
    else
      a[1] <=> b[1]
    end
  end
end

#origin_namesObject

Origin name needs to be serialized when content is empty, assuming a name is available, for list definitions with variable that is currently empty



191
192
193
194
195
196
197
# File 'lib/fable/ink_list.rb', line 191

def origin_names
  if self.list.any?
    @origin_names = self.list.map{|item, int_value| item.origin_name }.compact.uniq
  end

  return @origin_names
end

#origin_of_max_itemObject

Story has to set this so that the value knows its origin, necessary for certain operations (eg: iteracting with ints). Only the story has access to the full set of lists, so that the origin can be resolved from the origin_list_name



182
183
184
185
186
# File 'lib/fable/ink_list.rb', line 182

def origin_of_max_item
  return nil if origins.nil?
  max_origin_name = max_item.origin_name
  origins.find{|origin| origin.name == max_origin_name }
end

#set_initial_origin_name(initial_origin_name) ⇒ Object



199
200
201
# File 'lib/fable/ink_list.rb', line 199

def set_initial_origin_name(initial_origin_name)
  @origin_names = [initial_origin_name]
end

#set_initial_origin_names(initial_origin_names) ⇒ Object



203
204
205
# File 'lib/fable/ink_list.rb', line 203

def set_initial_origin_names(initial_origin_names)
  @origin_names = initial_origin_names
end

#to_sObject

Returns a string in the form “a, b, c” with the names of the items in the list, without the origin list definition names. Equivalent to writing #list in ink



421
422
423
# File 'lib/fable/ink_list.rb', line 421

def to_s
  ordered_items.map{|item, int_value| item.item_name }.join(", ")
end