slippy_tiles_scorer 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +12 -1
- data/README.md +17 -10
- data/Rakefile +4 -1
- data/lib/slippy_tiles_scorer/cluster.rb +48 -30
- data/lib/slippy_tiles_scorer/max_square.rb +69 -17
- data/lib/slippy_tiles_scorer/version.rb +1 -1
- data/lib/slippy_tiles_scorer.rb +8 -9
- data/test_helper.rb +18 -0
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce623fd11c56fc5615043d62fd196810c7c0caccd5f9d9789dfaa864954ed2ee
|
4
|
+
data.tar.gz: 0b7296978405915b2ee53d79da2a8e8c0cc15445f2b71ce5e396ca5fc7af5a39
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 746ffc5f6d06857aec00c471b3c671612debe31c22a2cdb1d73b1da3583ba253008cbea5e886addf44961019cd2435db6eccb7f70086f182e3cfa3440247a88a
|
7
|
+
data.tar.gz: 7e6bacaaab5a06b40d0fee46a3494f436a96b04e042a90bae45ae567ab1860b99499999e66089a372be84d733ae4f15683316748ee1835604c3358acdb3b2da8
|
data/.rubocop.yml
CHANGED
@@ -1,10 +1,19 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rake
|
3
|
+
- rubocop-performance
|
4
|
+
|
1
5
|
AllCops:
|
2
6
|
TargetRubyVersion: 3.0
|
7
|
+
NewCops: enable
|
3
8
|
Exclude:
|
4
|
-
|
9
|
+
- "*.gemspec"
|
10
|
+
- "./vendor/bundle/**/*"
|
5
11
|
|
6
12
|
Style/StringLiterals:
|
7
13
|
EnforcedStyle: double_quotes
|
8
14
|
|
9
15
|
Style/StringLiteralsInInterpolation:
|
10
16
|
EnforcedStyle: double_quotes
|
17
|
+
|
18
|
+
Naming/MethodParameterName:
|
19
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
## [0.
|
3
|
+
## [0.0.3] - 2024-11-26
|
4
|
+
|
5
|
+
- More tests, like a json file with test data.
|
6
|
+
- The `Rakefile` can be used to run the tests and check the code style.
|
7
|
+
- A CI pipeline runs the test suite and checks the code style for multiple Ruby versions >= v3.
|
8
|
+
- Rubocop was configured and some plugins were added to the `.rubocop.yml` file.
|
9
|
+
|
10
|
+
## [0.0.2] - 2024-11-26
|
11
|
+
|
12
|
+
- The result `Hash` of `#clusters` under the key `:clusters` is now an array of `[x, y]` and `x` and `y` being integer values.
|
13
|
+
|
14
|
+
## [0.0.1] - 2024-11-25
|
4
15
|
|
5
16
|
- Initial release
|
data/README.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# SlippyTilesScorer
|
2
2
|
|
3
|
+
> [!NOTE]
|
4
|
+
> This gem's API is not considered stable yet.
|
5
|
+
> Things might change in the future.
|
6
|
+
|
7
|
+
[![Gem Version](https://badge.fury.io/rb/slippy_tiles_scorer.svg)](https://badge.fury.io/rb/slippy_tiles_scorer) \
|
8
|
+
[![Ruby](https://github.com/simonneutert/slippy_tiles_scorer/actions/workflows/main.yml/badge.svg)](https://github.com/simonneutert/slippy_tiles_scorer/actions/workflows/main.yml)
|
9
|
+
|
3
10
|
Calculate scores of map tiles (x, y) on a map. The scores are total, (max)
|
4
11
|
clusters, and max squares.
|
5
12
|
|
@@ -7,18 +14,16 @@ To experiment with that code, run `bin/console` for an interactive prompt.
|
|
7
14
|
|
8
15
|
## Installation
|
9
16
|
|
10
|
-
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
11
|
-
|
12
17
|
Install the gem and add to the application's Gemfile by executing:
|
13
18
|
|
14
19
|
```bash
|
15
|
-
bundle add
|
20
|
+
bundle add slippy_tiles_scorer
|
16
21
|
```
|
17
22
|
|
18
23
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
19
24
|
|
20
25
|
```bash
|
21
|
-
gem install
|
26
|
+
gem install slippy_tiles_scorer
|
22
27
|
```
|
23
28
|
|
24
29
|
## Usage
|
@@ -43,7 +48,7 @@ collection_of_tiles.add([2, 0])
|
|
43
48
|
collection_of_tiles.add([2, 1])
|
44
49
|
collection_of_tiles.add([2, 2])
|
45
50
|
|
46
|
-
tile_scorer = SlippyTilesScorer::
|
51
|
+
tile_scorer = SlippyTilesScorer::Score.new(tiles_x_y: collection_of_tiles)
|
47
52
|
tile_scorer.valid? # => true
|
48
53
|
|
49
54
|
# the collection_of_tiles is a 3x3 map and will be used to calculate scores,
|
@@ -55,15 +60,17 @@ tile_scorer.visited # => 9
|
|
55
60
|
tile_scorer.clusters # =>
|
56
61
|
# {
|
57
62
|
# :clusters=>[
|
58
|
-
#
|
63
|
+
# [
|
64
|
+
# [0, 0], [1, 0], [0, 1], [1, 1], [0, 2], [1, 2], [2, 2], [2, 1], [2, 0]
|
65
|
+
# ]
|
59
66
|
# ],
|
60
67
|
# :cluster_tiles=>#<Set: {[1, 1]}>,
|
61
|
-
# :clusters_of_cluster_tiles=>[]
|
68
|
+
# :clusters_of_cluster_tiles=>[[[1, 1]]]
|
62
69
|
# }
|
63
70
|
|
64
71
|
# calculate max squares
|
65
|
-
tile_scorer.max_squares # => {:size=>
|
66
|
-
tile_scorer.max_squares(min_size: 4) # => {:size=>
|
72
|
+
tile_scorer.max_squares # => {:size=>0, top_left_tile_x_y=>#<Set: {[0, 0]}>}
|
73
|
+
tile_scorer.max_squares(min_size: 4) # => {:size=>0, top_left_tile_x_y=>#<Set: {}>}
|
67
74
|
```
|
68
75
|
|
69
76
|
### Optimized
|
@@ -72,7 +79,7 @@ An more computationally optimized example of how to use the `TileScorer` class,
|
|
72
79
|
|
73
80
|
```ruby
|
74
81
|
### OPTIMIZE COMPUTATION ###
|
75
|
-
tile_scorer = SlippyTilesScorer::
|
82
|
+
tile_scorer = SlippyTilesScorer::Score.new(tiles_x_y: collection_of_tiles)
|
76
83
|
# Optimize computation by calculating clusters first,
|
77
84
|
result_clusters = tile_scorer.clusters
|
78
85
|
clusters = result_clusters[:clusters]
|
data/Rakefile
CHANGED
@@ -1,68 +1,86 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
4
|
+
|
3
5
|
module SlippyTilesScorer
|
4
6
|
# finds clusters in a collection/tiles_x_y of points (x, y)
|
5
7
|
class Cluster
|
6
8
|
attr_accessor :tiles_x_y
|
7
9
|
|
8
10
|
def initialize(tiles_x_y: Set.new)
|
9
|
-
@tiles_x_y =
|
11
|
+
@tiles_x_y = tiles_x_y
|
10
12
|
@cluster_tiles = Set.new
|
11
|
-
@visited =
|
13
|
+
@visited = {}
|
12
14
|
@clusters = []
|
13
15
|
end
|
14
16
|
|
17
|
+
# @return [Hash] The clusters and the tiles in the clusters.
|
15
18
|
def clusters
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
find_cluster_around(
|
19
|
+
@tiles_x_y.each do |i|
|
20
|
+
next if visited?(i[0], i[1])
|
21
|
+
|
22
|
+
visit!(i[0], i[1])
|
23
|
+
find_cluster_around(i)
|
21
24
|
end
|
22
25
|
{ clusters: @clusters, cluster_tiles: @cluster_tiles }
|
23
26
|
end
|
24
27
|
|
25
28
|
private
|
26
29
|
|
30
|
+
# @param start [Array] The x and y coordinate of the start point.
|
31
|
+
# @return [Array<Array<Integer, Integer>>] The cluster of points.
|
27
32
|
def find_cluster_around(start)
|
28
|
-
cluster =
|
29
|
-
cluster.
|
33
|
+
cluster = []
|
34
|
+
cluster.push(start)
|
30
35
|
todo = [start]
|
36
|
+
broad_search!(todo, cluster)
|
31
37
|
|
32
|
-
cluster
|
33
|
-
|
34
|
-
@clusters.push(cluster) if cluster.size > 1
|
38
|
+
@clusters.push(cluster) if cluster.size.positive?
|
35
39
|
cluster
|
36
40
|
end
|
37
41
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
def broad_search(todo, cluster) # rubocop:disable Metrics/MethodLength
|
42
|
+
# @param todo [Array] The points to visit.
|
43
|
+
# @param cluster [Array] The cluster of points.
|
44
|
+
def broad_search!(todo, cluster) # rubocop:disable Metrics/CyclomaticComplexity
|
43
45
|
until todo.empty?
|
44
46
|
point = todo.pop
|
45
47
|
neighbors = neighbor_points(*point)
|
46
|
-
|
47
|
-
next if neighbors.empty?
|
48
|
-
|
49
|
-
@cluster_tiles.add(point) if neighbors_up_down_left_right?(neighbors)
|
48
|
+
neighbors_up_down_left_right?(neighbors) && @cluster_tiles.add(point)
|
50
49
|
neighbors.each do |neighbor|
|
50
|
+
next if visited?(neighbor[0], neighbor[1])
|
51
51
|
next unless @tiles_x_y.include?(neighbor)
|
52
|
-
next if @visited.include?(neighbor)
|
53
52
|
|
54
|
-
|
55
|
-
cluster.add(neighbor)
|
56
|
-
todo.push(neighbor)
|
53
|
+
visit!(*neighbor) && todo.push(neighbor) && cluster.push(neighbor)
|
57
54
|
end
|
58
55
|
end
|
59
|
-
cluster
|
60
56
|
end
|
61
57
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
58
|
+
# @param x [Integer] The x coordinate of the point.
|
59
|
+
# @param y [Integer] The y coordinate of the point.
|
60
|
+
# @return [Boolean] True if the point is visited.
|
61
|
+
def visit!(x, y)
|
62
|
+
@visited[x] ||= {}
|
63
|
+
@visited[x][y] = true
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param x [Integer] The x coordinate of the point.
|
67
|
+
# @param y [Integer] The y coordinate of the point.
|
68
|
+
# @return [Boolean|Nil] True if the point is visited.
|
69
|
+
def visited?(x, y)
|
70
|
+
@visited.dig(x, y)
|
71
|
+
end
|
72
|
+
|
73
|
+
# @param neighbors [Array] The neighbors of the point.
|
74
|
+
# @return [Boolean] True if all neighbors are in the tiles_x_y.
|
75
|
+
def neighbors_up_down_left_right?(neighbors)
|
76
|
+
neighbors.all? { |n| @tiles_x_y.include?(n) }
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param x [Integer] The x coordinate of the point.
|
80
|
+
# @param y [Integer] The y coordinate of the point.
|
81
|
+
# @return [Array<Array<Integer, Integer>>] The neighbors of the point.
|
82
|
+
def neighbor_points(x, y)
|
83
|
+
[[x - 1, y], [x + 1, y], [x, y + 1], [x, y - 1]]
|
66
84
|
end
|
67
85
|
end
|
68
86
|
end
|
@@ -1,54 +1,106 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
4
|
+
|
3
5
|
module SlippyTilesScorer
|
4
6
|
# finds the maximum square in a collection/tiles_x_y of points (x, y)
|
5
7
|
class MaxSquare
|
6
8
|
attr_accessor :tiles_x_y
|
7
9
|
|
8
10
|
def initialize(tiles_x_y: Set.new)
|
9
|
-
@tiles_x_y =
|
11
|
+
@tiles_x_y = tiles_x_y
|
12
|
+
@tiles_lut = nil
|
10
13
|
end
|
11
14
|
|
12
15
|
# @param min_size [Integer] The minimum size of the square, should be 3 or greater.
|
13
16
|
# As the smallest square is 3x3 and therefor a cluster tile needs to be the center.
|
14
17
|
# @return [Hash] The size of the square and the points of the square.
|
15
18
|
def max_squares(min_size: 3)
|
16
|
-
|
19
|
+
@max_size_found = 0
|
20
|
+
raise ArgumentError, "min_size must be 2 or greater" if min_size < 2
|
21
|
+
|
22
|
+
result = max_square_result(min_size: min_size)
|
23
|
+
@max_size_found = 0
|
24
|
+
@tiles_lut = nil
|
25
|
+
result
|
26
|
+
end
|
17
27
|
|
18
|
-
|
28
|
+
# @param min_size [Integer] The minimum size of the square, should be 3 or greater.
|
29
|
+
# @return [Hash] The size of the square and the points of the square.
|
30
|
+
def max_square_result(min_size: 3)
|
31
|
+
result = { size: @max_size_found, top_left_tile_x_y: Set.new }
|
19
32
|
@tiles_x_y.each do |(x, y)|
|
20
|
-
raise ArgumentError,
|
33
|
+
raise ArgumentError, "x and y must be greater than or equal to 0" if x.negative? || y.negative?
|
21
34
|
|
22
35
|
steps = max_square(x: x, y: y)
|
23
|
-
track_result(result: result, steps: steps, x: x, y: y) if steps >= min_size
|
36
|
+
result = track_result(result: result, steps: steps, x: x, y: y) if steps >= min_size
|
24
37
|
end
|
25
38
|
result
|
26
39
|
end
|
27
40
|
|
28
|
-
|
41
|
+
# @param x [Integer] The x coordinate of the top left tile.
|
42
|
+
# @param y [Integer] The y coordinate of the top left tile.
|
43
|
+
# @return [Integer] The size of the square.
|
44
|
+
def max_square(x:, y:)
|
29
45
|
steps = 1
|
30
46
|
steps += 1 while steps_fulfilled?(x: x, y: y, steps: steps)
|
31
47
|
steps
|
32
48
|
end
|
33
49
|
|
34
|
-
|
35
|
-
|
50
|
+
# @param x [Integer] The x coordinate of the top left tile.
|
51
|
+
# @param y [Integer] The y coordinate of the top left tile.
|
52
|
+
# @param steps [Integer] The size of the square.
|
53
|
+
# @return [Boolean] True if all steps are fulfilled.
|
54
|
+
def steps_fulfilled?(x:, y:, steps:)
|
55
|
+
in_lut?(x: x + steps, y: y + steps) &&
|
36
56
|
(0...steps).all? do |i|
|
37
|
-
|
38
|
-
|
57
|
+
in_lut?(x: x + steps, y: y + i) &&
|
58
|
+
in_lut?(x: x + i, y: y + steps)
|
39
59
|
end
|
40
60
|
end
|
41
61
|
|
42
62
|
private
|
43
63
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
64
|
+
# This is as fast as it gets! Do not implement `#nil?
|
65
|
+
#
|
66
|
+
# @param x [Integer] The x coordinate of the top left tile.
|
67
|
+
# @param y [Integer] The y coordinate of the top left tile.
|
68
|
+
# @return [Boolean|Nil] True if the tile is in the tiles_lut.
|
69
|
+
def in_lut?(x:, y:)
|
70
|
+
unless @tiles_lut
|
71
|
+
@tiles_lut = {}
|
72
|
+
@tiles_x_y.each do |(x, y)|
|
73
|
+
@tiles_lut[x] ||= {}
|
74
|
+
@tiles_lut[x][y] = true
|
75
|
+
end
|
51
76
|
end
|
77
|
+
@tiles_lut.dig(x, y)
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param result [Hash] The result hash.
|
81
|
+
# @param steps [Integer] The size of the square.
|
82
|
+
# @param x [Integer] The x coordinate of the top left tile.
|
83
|
+
# @param y [Integer] The y coordinate of the top left tile.
|
84
|
+
# @return [Hash] The updated result hash.
|
85
|
+
def track_result(result:, steps:, x:, y:)
|
86
|
+
if steps > @max_size_found
|
87
|
+
@max_size_found = steps
|
88
|
+
result = { size: @max_size_found, top_left_tile_x_y: Set.new }
|
89
|
+
update_result!(result: result, steps: steps, x: x, y: y)
|
90
|
+
elsif steps == @max_size_found
|
91
|
+
update_result!(result: result, steps: steps, x: x, y: y)
|
92
|
+
end
|
93
|
+
result
|
94
|
+
end
|
95
|
+
|
96
|
+
# @param result [Hash] The result hash.
|
97
|
+
# @param steps [Integer] The size of the square.
|
98
|
+
# @param x [Integer] The x coordinate of the top left tile.
|
99
|
+
# @param y [Integer] The y coordinate of the top left tile.
|
100
|
+
# @return [Hash] The updated result hash.
|
101
|
+
def update_result!(result:, steps:, x:, y:)
|
102
|
+
result[:size] = steps
|
103
|
+
result[:top_left_tile_x_y] << [x, y]
|
52
104
|
result
|
53
105
|
end
|
54
106
|
end
|
data/lib/slippy_tiles_scorer.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "set"
|
3
4
|
require_relative "slippy_tiles_scorer/version"
|
4
|
-
require_relative "
|
5
|
-
require_relative "
|
5
|
+
require_relative "slippy_tiles_scorer/cluster"
|
6
|
+
require_relative "slippy_tiles_scorer/max_square"
|
6
7
|
|
7
8
|
module SlippyTilesScorer
|
8
9
|
class Error < StandardError; end
|
@@ -17,11 +18,9 @@ module SlippyTilesScorer
|
|
17
18
|
|
18
19
|
def valid?
|
19
20
|
return true if @tiles_x_y.empty?
|
20
|
-
|
21
21
|
raise ArgumentError, "@tiles_x_y must be a Set" unless @tiles_x_y.is_a?(Set)
|
22
22
|
|
23
|
-
set_of_arrays?
|
24
|
-
set_of_arrays_of_integers?
|
23
|
+
set_of_arrays? && set_of_arrays_of_integers?
|
25
24
|
end
|
26
25
|
|
27
26
|
def clusters(tiles_x_y: @tiles_x_y)
|
@@ -32,7 +31,7 @@ module SlippyTilesScorer
|
|
32
31
|
result
|
33
32
|
end
|
34
33
|
|
35
|
-
def max_square(x:, y:, tiles_x_y: @tiles_x_y)
|
34
|
+
def max_square(x:, y:, tiles_x_y: @tiles_x_y)
|
36
35
|
SlippyTilesScorer::MaxSquare.new(tiles_x_y: tiles_x_y).max_square(x: x, y: y)
|
37
36
|
end
|
38
37
|
|
@@ -40,7 +39,7 @@ module SlippyTilesScorer
|
|
40
39
|
SlippyTilesScorer::MaxSquare.new(tiles_x_y: tiles_x_y).max_squares(min_size: min_size)
|
41
40
|
end
|
42
41
|
|
43
|
-
def steps_fulfilled?(x:, y:, steps:)
|
42
|
+
def steps_fulfilled?(x:, y:, steps:)
|
44
43
|
SlippyTilesScorer::MaxSquare.new(tiles_x_y: @tiles_x_y).steps_fulfilled?(x: x, y: y, steps: steps)
|
45
44
|
end
|
46
45
|
|
@@ -51,13 +50,13 @@ module SlippyTilesScorer
|
|
51
50
|
private
|
52
51
|
|
53
52
|
def set_of_arrays?
|
54
|
-
return if @tiles_x_y.all? { |point| point.is_a?(Array) && point.size == 2 }
|
53
|
+
return true if @tiles_x_y.all? { |point| point.is_a?(Array) && point.size == 2 }
|
55
54
|
|
56
55
|
raise ArgumentError, "each point must be an array with two elements"
|
57
56
|
end
|
58
57
|
|
59
58
|
def set_of_arrays_of_integers?
|
60
|
-
return if @tiles_x_y.all? { |point| point.all?
|
59
|
+
return true if @tiles_x_y.all? { |point| point.all?(Integer) }
|
61
60
|
|
62
61
|
raise ArgumentError, "each point must be an array with two integers"
|
63
62
|
end
|
data/test_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SlippyTilesScorer
|
4
|
+
# TestHelper class to help with testing
|
5
|
+
class TestHelper
|
6
|
+
class << self
|
7
|
+
def stub_tiles_x_y(service:, size: 50)
|
8
|
+
service.tiles_x_y = Set.new
|
9
|
+
(0...size).each do |i|
|
10
|
+
(0...size).each do |j|
|
11
|
+
service.tiles_x_y.add([i, j])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
service
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slippy_tiles_scorer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simon Neutert
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-11-
|
12
|
-
dependencies:
|
11
|
+
date: 2024-11-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: set
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
13
27
|
description: For a given set of slippy tiles, this gem calculates the score of the
|
14
28
|
tiles, like total tiles, clusters and max squares.
|
15
29
|
email:
|
@@ -27,6 +41,7 @@ files:
|
|
27
41
|
- lib/slippy_tiles_scorer/max_square.rb
|
28
42
|
- lib/slippy_tiles_scorer/version.rb
|
29
43
|
- sig/slippy_tiles_scorer.rbs
|
44
|
+
- test_helper.rb
|
30
45
|
homepage: https://github.com/simonneutert/slippy_tiles_scorer
|
31
46
|
licenses: []
|
32
47
|
metadata:
|