Module: Lich::Common::MapBase::InstanceMethods

Defined in:
documented/common/map/map_base.rb

Overview

Instance methods for map objects.

These methods provide functionality for individual map instances.

Instance Method Summary collapse

Instance Method Details

#descString

Returns the description of the map room.

Returns:

  • (String)

    the description of the room



455
456
457
# File 'documented/common/map/map_base.rb', line 455

def desc
  @description
end

#dijkstra(destination = nil) ⇒ Array

Computes the shortest path to a destination room using Dijkstra's algorithm.

Parameters:

  • destination (String, nil) (defaults to: nil)

    the destination room identifier, or nil for all reachable rooms

Returns:

  • (Array)

    the shortest path and distances

Raises:

  • StandardError if an error occurs during computation



332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
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
397
# File 'documented/common/map/map_base.rb', line 332

def dijkstra(destination = nil)
  self.class.load unless self.class.loaded?
  source = @id
  visited = {}
  shortest_distances_hash = {}
  previous_hash = {}

  pq = MinHeap.new
  pq.push(0, source)
  shortest_distances_hash[source] = 0

  check_destination = proc do |v, dist|
    case destination
    when Integer
      v == destination
    when Array
      destination.include?(v) && dist < 20
    else
      false
    end
  end

  until pq.empty?
    current_dist, v = pq.pop

    next if visited[v]
    break if check_destination.call(v, current_dist)

    visited[v] = true

    self.class.list[v].wayto.keys.each do |adj_room|
      adj_room_i = adj_room.to_i
      next if visited[adj_room_i]

      edge_weight = if self.class.list[v].timeto[adj_room].is_a?(StringProc)
                      self.class.list[v].timeto[adj_room].call
                    else
                      self.class.list[v].timeto[adj_room]
                    end

      next unless edge_weight

      new_distance = current_dist + edge_weight

      if !shortest_distances_hash[adj_room_i] || shortest_distances_hash[adj_room_i] > new_distance
        shortest_distances_hash[adj_room_i] = new_distance
        previous_hash[adj_room_i] = v
        pq.push(new_distance, adj_room_i)
      end
    end
  end

  # Convert hashes back to arrays for backward compatibility
  max_room_id = [previous_hash.keys.max, shortest_distances_hash.keys.max].compact.max || 0
  previous = Array.new(max_room_id + 1)
  shortest_distances = Array.new(max_room_id + 1)

  previous_hash.each { |key, value| previous[key] = value }
  shortest_distances_hash.each { |key, value| shortest_distances[key] = value }

  [previous, shortest_distances]
rescue StandardError => e
  echo "Map.dijkstra: error: #{e}"
  respond e.backtrace
  nil
end

#find_all_nearest_by_tag(tag_name) ⇒ Array<Integer>

Finds all nearest rooms with a specified tag.

Parameters:

  • tag_name (String)

    the tag to search for

Returns:

  • (Array<Integer>)

    an array of IDs of the nearest rooms with the specified tag



431
432
433
434
435
436
437
# File 'documented/common/map/map_base.rb', line 431

def find_all_nearest_by_tag(tag_name)
  target_list = []
  self.class.list.each { |room| target_list.push(room.id) if room.tags.include?(tag_name) }
  _, shortest_distances = self.class.dijkstra(@id)
  target_list.delete_if { |room_num| shortest_distances[room_num].nil? }
  target_list.sort { |a, b| shortest_distances[a] <=> shortest_distances[b] }
end

#find_nearest(target_list) ⇒ Integer

Finds the nearest room from a list of target rooms.

Parameters:

  • target_list (Array<String>)

    an array of room identifiers to search from

Returns:

  • (Integer)

    the ID of the nearest room



442
443
444
445
446
447
448
449
450
451
# File 'documented/common/map/map_base.rb', line 442

def find_nearest(target_list)
  target_list = target_list.collect(&:to_i)
  if target_list.include?(@id)
    @id
  else
    _, shortest_distances = self.class.dijkstra(@id, target_list)
    valid_rooms = target_list.select { |room_num| shortest_distances[room_num].is_a?(Numeric) }
    valid_rooms.min_by { |room_num| shortest_distances[room_num] }
  end
end

#find_nearest_by_tag(tag_name) ⇒ Integer

Finds the nearest room with a specified tag.

Parameters:

  • tag_name (String)

    the tag to search for

Returns:

  • (Integer)

    the ID of the nearest room with the specified tag



416
417
418
419
420
421
422
423
424
425
426
# File 'documented/common/map/map_base.rb', line 416

def find_nearest_by_tag(tag_name)
  target_list = []
  self.class.list.each { |room| target_list.push(room.id) if room.tags.include?(tag_name) }
  _, shortest_distances = self.class.dijkstra(@id, target_list)
  if target_list.include?(@id)
    @id
  else
    target_list.delete_if { |room_num| shortest_distances[room_num].nil? }
    target_list.sort { |a, b| shortest_distances[a] <=> shortest_distances[b] }.first
  end
end

#geoObject



489
490
491
# File 'documented/common/map/map_base.rb', line 489

def geo
  nil
end

#inside?Boolean

Returns:

  • (Boolean)


288
289
290
# File 'documented/common/map/map_base.rb', line 288

def inside?
  !outside?
end

#inspectObject



292
293
294
295
296
# File 'documented/common/map/map_base.rb', line 292

def inspect
  instance_variables.collect do |var|
    "#{var}=#{instance_variable_get(var).inspect}"
  end.join("\n")
end

#json_extra_fieldsObject



298
299
300
# File 'documented/common/map/map_base.rb', line 298

def json_extra_fields
  {}
end

#map_nameString

Returns the name of the map.

Returns:

  • (String)

    the name of the map



461
462
463
# File 'documented/common/map/map_base.rb', line 461

def map_name
  @image
end

#map_roomsizeInteger?

Returns the size of the map room.

Returns:

  • (Integer, nil)

    the size of the room, or nil if not available



483
484
485
486
487
# File 'documented/common/map/map_base.rb', line 483

def map_roomsize
  return nil if @image_coords.nil?

  image_coords[2] - image_coords[0]
end

#map_xInteger?

Returns the X coordinate of the map's center.

Returns:

  • (Integer, nil)

    the X coordinate, or nil if not available



467
468
469
470
471
# File 'documented/common/map/map_base.rb', line 467

def map_x
  return nil if @image_coords.nil?

  ((image_coords[0] + image_coords[2]) / 2.0).round
end

#map_yInteger?

Returns the Y coordinate of the map's center.

Returns:

  • (Integer, nil)

    the Y coordinate, or nil if not available



475
476
477
478
479
# File 'documented/common/map/map_base.rb', line 475

def map_y
  return nil if @image_coords.nil?

  ((image_coords[1] + image_coords[3]) / 2.0).round
end

#outside?Boolean

Returns:

  • (Boolean)


282
283
284
285
286
# File 'documented/common/map/map_base.rb', line 282

def outside?
  return false if @paths.nil? || @paths.empty?

  @paths.last =~ /^Obvious paths:/ ? true : false
end

#path_to(destination) ⇒ Array<Integer>?

Finds the path to a specified destination room.

Parameters:

  • destination (String)

    the destination room identifier

Returns:

  • (Array<Integer>, nil)

    the path as an array of room IDs, or nil if no path exists



402
403
404
405
406
407
408
409
410
411
# File 'documented/common/map/map_base.rb', line 402

def path_to(destination)
  self.class.load unless self.class.loaded?
  destination = destination.to_i
  previous, = dijkstra(destination)
  return nil unless previous[destination]

  path = [destination]
  path.push(previous[path[-1]]) until previous[path[-1]] == @id
  path.reverse
end

#to_iObject



278
279
280
# File 'documented/common/map/map_base.rb', line 278

def to_i
  @id
end

#to_json(*_args) ⇒ String

Converts the map instance to a JSON representation.

Parameters:

  • args (Array)

    optional arguments for JSON generation

Returns:

  • (String)

    the JSON representation of the map



305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'documented/common/map/map_base.rb', line 305

def to_json(*_args)
  mapjson = {
    id: @id,
    title: @title,
    description: @description,
    paths: @paths,
    location: @location,
    climate: @climate,
    terrain: @terrain,
    wayto: @wayto&.sort_by { |k, _v| k.to_i }&.to_h,
    timeto: @timeto&.sort_by { |k, _v| k.to_i }&.to_h,
    image: @image,
    image_coords: @image_coords,
    tags: @tags&.sort_by(&:downcase),
    check_location: @check_location,
    unique_loot: @unique_loot,
    uid: @uid
  }
  mapjson.merge!(json_extra_fields)
  mapjson.delete_if { |_a, b| b.nil? || (b.is_a?(Array) && b.empty?) }
  JSON.pretty_generate(mapjson)
end