slippy_tiles_scorer 0.0.2 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 95be9f898e6231b3595f43b3b35baf834e5eba161e78db33b2eb14c7baa19975
4
- data.tar.gz: 785f9286455947d79581de9cef5ffc2cde4ecd8b1860f7ab0494c6ae47386ff9
3
+ metadata.gz: 3a911e3fb4c9a3d30d90e52664a3619f2c208581a0a31ee938ab4c44444d9608
4
+ data.tar.gz: 8093375ef475c76b1c996b31335a7b151e850778c8cd9c285411b0d33926e7a7
5
5
  SHA512:
6
- metadata.gz: f9af7b7e325f515220925b5361cb5c20aefc00add5742539d2b0dfd42a5b9f1d996b880a6d739180ac84d9a8715a68b08325ebcf34008a52a311a9cbe5b51899
7
- data.tar.gz: 85ffe608074ddf335004e48dee35feef330bea221de5fdd6f4deab9213df1d266cf29c5edf9d45d07316c588f4d3a62d19e0bd3e7689809cf328375f595a53d2
6
+ metadata.gz: ad02a2cbdda34c8976cdd4cea4a9d90c5c9b80afe3c77c55eec578f054de4c4d89272b1e10eaa47f253184ea96d4fa2716fc16873d7ae51dfe2bf836e47d61e4
7
+ data.tar.gz: c55a90cf772a57db236e2b182ac6853bc0137685ca92567c48883a1e897e05d5e0bb38bde02e50a63832793b117b1ba619c2bdf255530668fd51b4031d1d9d9d
data/.rubocop.yml CHANGED
@@ -1,10 +1,20 @@
1
+ require:
2
+ - rubocop-rake
3
+ plugins:
4
+ - rubocop-performance
5
+
1
6
  AllCops:
2
7
  TargetRubyVersion: 3.0
8
+ NewCops: enable
3
9
  Exclude:
4
- '*.gemspec'
10
+ - "*.gemspec"
11
+ - "./vendor/bundle/**/*"
5
12
 
6
13
  Style/StringLiterals:
7
14
  EnforcedStyle: double_quotes
8
15
 
9
16
  Style/StringLiteralsInInterpolation:
10
17
  EnforcedStyle: double_quotes
18
+
19
+ Naming/MethodParameterName:
20
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.1.0] - 2024-11-25
3
+ - [#5](https://github.com/simonneutert/slippy_tiles_scorer/pull/5) - Reworks maximum-square detection with a sparse dynamic-programming algorithm. Adds validation for negative coordinates and minimum square sizes, and expands coverage for multiple and incomplete squares.
4
+ - [#4](https://github.com/simonneutert/slippy_tiles_scorer/pull/4) - Runs a benchmark before running the tests.
5
+ - [#3](https://github.com/simonneutert/slippy_tiles_scorer/pull/3) - 100 % test coverage: lines and branches. Adds a badge to the README. Adds gem release notes to the README.
6
+
7
+ ## [0.0.3] - 2024-11-26
8
+
9
+ - More tests, like a json file with test data.
10
+ - The `Rakefile` can be used to run the tests and check the code style.
11
+ - A CI pipeline runs the test suite and checks the code style for multiple Ruby
12
+ versions >= v3.
13
+ - Rubocop was configured and some plugins were added to the `.rubocop.yml` file.
14
+
15
+ ## [0.0.2] - 2024-11-26
16
+
17
+ - The result `Hash` of `#clusters` under the key `:clusters` is now an array of
18
+ `[x, y]` and `x` and `y` being integer values.
19
+
20
+ ## [0.0.1] - 2024-11-25
4
21
 
5
22
  - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Simon Neutert
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # SlippyTilesScorer
2
2
 
3
- > [!NOTE]
4
- > This gem's API is not considered stable yet.
5
- > Things might change in the future.
3
+ > [!NOTE]\
4
+ > This gem's API is not considered stable yet. Things might change in the
5
+ > future.
6
6
 
7
- [![Gem Version](https://badge.fury.io/rb/slippy_tiles_scorer.svg)](https://badge.fury.io/rb/slippy_tiles_scorer) \
7
+ [![Gem Version](https://badge.fury.io/rb/slippy_tiles_scorer.svg)](https://badge.fury.io/rb/slippy_tiles_scorer)\
8
+ ![Coverage](https://github.com/simonneutert/slippy_tiles_scorer/blob/main/coverage_badge.svg)\
8
9
  [![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
10
 
10
11
  Calculate scores of map tiles (x, y) on a map. The scores are total, (max)
@@ -12,6 +13,8 @@ clusters, and max squares.
12
13
 
13
14
  To experiment with that code, run `bin/console` for an interactive prompt.
14
15
 
16
+ Supports Ruby >= 3.0, JRuby >= 9.4 and latest TruffleRuby (with GraalVM).
17
+
15
18
  ## Installation
16
19
 
17
20
  Install the gem and add to the application's Gemfile by executing:
@@ -20,7 +23,8 @@ Install the gem and add to the application's Gemfile by executing:
20
23
  bundle add slippy_tiles_scorer
21
24
  ```
22
25
 
23
- If bundler is not being used to manage dependencies, install the gem by executing:
26
+ If bundler is not being used to manage dependencies, install the gem by
27
+ executing:
24
28
 
25
29
  ```bash
26
30
  gem install slippy_tiles_scorer
@@ -35,7 +39,7 @@ Find some examples below.
35
39
  A simple example of how to use the `TileScorer` class, when tinkering in a REPL.
36
40
 
37
41
  ```ruby
38
- require 'tile_scorer'
42
+ require 'slippy_tiles_scorer'
39
43
 
40
44
  collection_of_tiles = Set.new
41
45
  collection_of_tiles.add([0, 0])
@@ -75,7 +79,8 @@ tile_scorer.max_squares(min_size: 4) # => {:size=>0, top_left_tile_x_y=>#<Set: {
75
79
 
76
80
  ### Optimized
77
81
 
78
- An more computationally optimized example of how to use the `TileScorer` class, when using the code in production.
82
+ An more computationally optimized example of how to use the `TileScorer` class,
83
+ when using the code in production.
79
84
 
80
85
  ```ruby
81
86
  ### OPTIMIZE COMPUTATION ###
@@ -101,10 +106,46 @@ puts max_squares # =>[{:size=>3, top_left_tile_x_y=>#<Set: {[0, 0]}>}]
101
106
 
102
107
  ## Development
103
108
 
104
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
109
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run
110
+ `rake test` to run the tests. You can also run `bin/console` for an interactive
111
+ prompt that will allow you to experiment.
105
112
 
106
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
113
+ To install this gem onto your local machine, run `bundle exec rake install`. To
114
+ release a new version, update the version number in `version.rb`, and then run
115
+ `bundle exec rake release`, which will create a git tag for the version, push
116
+ git commits and the created tag, and push the `.gem` file to
117
+ [rubygems.org](https://rubygems.org).
107
118
 
108
119
  ## Contributing
109
120
 
110
- Bug reports and pull requests are welcome on GitHub at https://github.com/simonneutert/slippy_tiles_scorer.
121
+ Bug reports and pull requests are welcome on GitHub at
122
+ https://github.com/simonneutert/slippy_tiles_scorer.
123
+
124
+ ## Release new Gem version
125
+
126
+ To release a new version of the gem, follow these steps:
127
+
128
+ ### Pre-release
129
+
130
+ 1. Make sure the tests pass and coverage badge is updated: `$ bundle exec rake`.
131
+ 2. Update the version number in `lib/slippy_tiles_scorer/version.rb`.
132
+ 3. Run `$ bundle install`.
133
+ 4. Check the `README.md` for correctness.
134
+ 5. Make sure the `CHANGELOG.md` is up to date.
135
+ 6. All changes are committed and pushed to the main branch.
136
+
137
+ ### Release
138
+
139
+ 1. Checkout the main branch: `$ git checkout main`.
140
+ 2. Run the tests: `$ bundle exec rake` (just for safety).
141
+ 3. Run `$ bundle exec rake release`.
142
+
143
+ ### Post-release
144
+
145
+ Still being on the main branch:
146
+
147
+ 1. Update the version number in `lib/slippy_tiles_scorer/version.rb` to the next
148
+ development version.
149
+ 2. Run `$ bundle install`.
150
+ 3. Commit the changes: `$ git commit -am "Bump version to vX.Y.Z"`.
151
+ 4. Push the changes: `$ git push`.
data/Rakefile CHANGED
@@ -4,6 +4,14 @@ require "bundler/gem_tasks"
4
4
  require "rubocop/rake_task"
5
5
  RuboCop::RakeTask.new
6
6
 
7
- task default: %i[test rubocop]
7
+ task default: %i[test rubocop copy_coverage]
8
8
 
9
- # TODO: add more tasks here
9
+ desc "Run tests"
10
+ task :test do
11
+ sh "ruby test_runner.rb"
12
+ end
13
+
14
+ desc "Copy coverage report to public"
15
+ task :copy_coverage do
16
+ FileUtils.cp("coverage/coverage_badge_total.svg", "./coverage_badge.svg")
17
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rubocop/rake_task"
5
+ RuboCop::RakeTask.new
6
+
7
+ task default: %i[test]
8
+
9
+ desc "Run tests"
10
+ task :test do
11
+ sh "ruby test_runner.rb"
12
+ end
@@ -0,0 +1,35 @@
1
+ <svg contentScriptType="text/ecmascript" contentStyleType="text/css" preserveAspectRatio="xMidYMid meet" version="1.0" height="20" width="120"
2
+ xmlns="http://www.w3.org/2000/svg"
3
+ xmlns:xlink="http://www.w3.org/1999/xlink">
4
+
5
+
6
+ <linearGradient id="smooth" x2="0" y2="120">
7
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
8
+ <stop offset="1" stop-opacity=".1"/>
9
+ </linearGradient>
10
+ <clipPath id="round">
11
+ <rect height="20" width="120" rx="3" fill="#fff"/>
12
+ </clipPath>
13
+ <g clip-path="url(#round)">
14
+ <rect height="20" width="60" fill="#555"/>
15
+ <rect x="60" height="20" width="60" fill="#cccc00"/>
16
+ <rect height="20" width="120" fill="url(#smooth)"/>
17
+ </g>
18
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,sans-serif" font-size="11">
19
+ <text x="30" y="15" fill="#010101" fill-opacity="0.3">
20
+ scov total
21
+ </text>
22
+ <text x="30" y="14">
23
+ scov total
24
+ </text>
25
+ </g>
26
+ <g fill="#fff" text-anchor="middle" font-family="Verdana,sans-serif" font-size="11">
27
+ <text x="90" y="15" fill="#010101" fill-opacity="0.3">
28
+ 100%
29
+ </text>
30
+ <text x="90" y="14">
31
+ 100%
32
+ </text>
33
+ </g>
34
+
35
+ </svg>
@@ -1,5 +1,7 @@
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
@@ -12,9 +14,11 @@ module SlippyTilesScorer
12
14
  @clusters = []
13
15
  end
14
16
 
17
+ # @return [Hash] The clusters and the tiles in the clusters.
15
18
  def clusters
16
19
  @tiles_x_y.each do |i|
17
20
  next if visited?(i[0], i[1])
21
+
18
22
  visit!(i[0], i[1])
19
23
  find_cluster_around(i)
20
24
  end
@@ -23,21 +27,21 @@ module SlippyTilesScorer
23
27
 
24
28
  private
25
29
 
30
+ # @param start [Array] The x and y coordinate of the start point.
31
+ # @return [Array<Array<Integer, Integer>>] The cluster of points.
26
32
  def find_cluster_around(start)
27
33
  cluster = []
28
34
  cluster.push(start)
29
35
  todo = [start]
30
36
  broad_search!(todo, cluster)
31
37
 
32
- @clusters.push(cluster) if cluster.size > 0
38
+ @clusters.push(cluster)
33
39
  cluster
34
40
  end
35
41
 
36
- def neighbors_up_down_left_right?(neighbors)
37
- neighbors.all? { |n| @tiles_x_y.include?(n) }
38
- end
39
-
40
- 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
41
45
  until todo.empty?
42
46
  point = todo.pop
43
47
  neighbors = neighbor_points(*point)
@@ -45,20 +49,36 @@ module SlippyTilesScorer
45
49
  neighbors.each do |neighbor|
46
50
  next if visited?(neighbor[0], neighbor[1])
47
51
  next unless @tiles_x_y.include?(neighbor)
52
+
48
53
  visit!(*neighbor) && todo.push(neighbor) && cluster.push(neighbor)
49
54
  end
50
55
  end
51
56
  end
52
57
 
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.
53
61
  def visit!(x, y)
54
62
  @visited[x] ||= {}
55
63
  @visited[x][y] = true
56
64
  end
57
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.
58
69
  def visited?(x, y)
59
70
  @visited.dig(x, y)
60
71
  end
61
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.
62
82
  def neighbor_points(x, y)
63
83
  [[x - 1, y], [x + 1, y], [x, y + 1], [x, y - 1]]
64
84
  end
@@ -1,72 +1,157 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "set"
4
+
3
5
  module SlippyTilesScorer
4
- # finds the maximum square in a collection/tiles_x_y of points (x, y)
6
+ # Finds the maximum fully-filled square in a collection of x/y tiles.
5
7
  class MaxSquare
6
- attr_accessor :tiles_x_y
8
+ attr_reader :tiles_x_y
7
9
 
8
10
  def initialize(tiles_x_y: Set.new)
11
+ self.tiles_x_y = tiles_x_y
12
+ end
13
+
14
+ def tiles_x_y=(tiles_x_y)
9
15
  @tiles_x_y = tiles_x_y
10
- @tiles_lut = nil
16
+ @square_scores = nil
11
17
  end
12
18
 
13
- # @param min_size [Integer] The minimum size of the square, should be 3 or greater.
14
- # As the smallest square is 3x3 and therefor a cluster tile needs to be the center.
15
- # @return [Hash] The size of the square and the points of the square.
19
+ # Finds all largest fully-filled squares.
20
+ #
21
+ # @param min_size [Integer] Minimum square size to report.
22
+ # @return [Hash] Maximum size and its top-left tile coordinates.
16
23
  def max_squares(min_size: 3)
17
- @max_size_found = 0
18
- raise ArgumentError, 'min_size must be 2 or greater' if min_size < 2
19
-
20
- result = { size: @max_size_found, top_left_tile_x_y: Set.new }
21
- @tiles_x_y.each do |(x, y)|
22
- raise ArgumentError, 'x and y must be greater than or equal to 0' if x.negative? || y.negative?
23
-
24
- steps = max_square(x: x, y: y)
25
- next if steps < min_size
26
- if steps > @max_size_found
27
- @max_size_found = steps
28
- result = { size: @max_size_found, top_left_tile_x_y: Set.new }
29
- track_result(result: result, steps: steps, x: x, y: y)
30
- elsif steps == @max_size_found
31
- result ||= { size: @max_size_found, top_left_tile_x_y: Set.new }
32
- track_result(result: result, steps: steps, x: x, y: y)
24
+ # Rebuild for every scoring run because tiles_x_y may be a mutable Set.
25
+ @square_scores = nil
26
+
27
+ max_square_result(min_size: min_size)
28
+ end
29
+
30
+ # @param min_size [Integer] Minimum square size to report.
31
+ # @return [Hash] Maximum size and its top-left tile coordinates.
32
+ def max_square_result(min_size: 3) # rubocop:disable Metrics/MethodLength
33
+ raise ArgumentError, "min_size must be 2 or greater" if min_size < 2
34
+
35
+ max_size = 0
36
+ top_left_tiles = Set.new
37
+
38
+ square_scores.each do |y, row|
39
+ row.each do |x, size|
40
+ next if size < min_size
41
+
42
+ if size > max_size
43
+ max_size = size
44
+ top_left_tiles.clear
45
+ top_left_tiles << [x, y]
46
+ elsif size == max_size
47
+ top_left_tiles << [x, y]
48
+ end
33
49
  end
34
50
  end
35
- @tiles_lut = nil
36
- result
51
+
52
+ {
53
+ size: max_size,
54
+ top_left_tile_x_y: top_left_tiles
55
+ }
37
56
  end
38
57
 
39
- def max_square(x:, y:) # rubocop:disable Naming/MethodParameterName
40
- steps = 1
41
- steps += 1 while steps_fulfilled?(x: x, y: y, steps: steps)
42
- steps
58
+ # Returns the size of the largest fully-filled square whose
59
+ # top-left tile is x/y.
60
+ #
61
+ # @param x [Integer] X coordinate of the top-left tile.
62
+ # @param y [Integer] Y coordinate of the top-left tile.
63
+ # @return [Integer] Square size, or 0 if the tile does not exist.
64
+ def max_square(x:, y:)
65
+ row = square_scores[y]
66
+
67
+ return 0 unless row
68
+
69
+ row[x] || 0
43
70
  end
44
71
 
45
- def steps_fulfilled?(x:, y:, steps:) # rubocop:disable Naming/MethodParameterName
46
- in_lut?(x: x + steps, y: y + steps) &&
47
- (0...steps).all? do |i|
48
- in_lut?(x: x + steps, y: y + i) &&
49
- in_lut?(x: x + i, y: y + steps)
50
- end
72
+ # Reports whether the square starting at x/y can grow beyond +steps+.
73
+ #
74
+ # For example:
75
+ # steps_fulfilled?(x: 0, y: 0, steps: 2)
76
+ #
77
+ # is true when a fully-filled square of at least 3x3 exists there.
78
+ #
79
+ # @param x [Integer] X coordinate of the top-left tile.
80
+ # @param y [Integer] Y coordinate of the top-left tile.
81
+ # @param steps [Integer] Current square size.
82
+ # @return [Boolean]
83
+ def steps_fulfilled?(x:, y:, steps:)
84
+ max_square(x: x, y: y) > steps
51
85
  end
52
86
 
53
87
  private
54
88
 
55
- def in_lut?(x:, y:)
56
- unless @tiles_lut
57
- @tiles_lut = {}
58
- @tiles_x_y.each do |(x, y)|
59
- @tiles_lut[x] ||= {}
60
- @tiles_lut[x][y] = true
89
+ # Builds a sparse dynamic-programming lookup.
90
+ #
91
+ # For every existing tile:
92
+ #
93
+ # score(x, y) =
94
+ # 1 + min(
95
+ # score(x + 1, y),
96
+ # score(x, y + 1),
97
+ # score(x + 1, y + 1)
98
+ # )
99
+ #
100
+ # A score of N guarantees that every tile in the N x N square
101
+ # beginning at x/y exists.
102
+ #
103
+ # @return [Hash<Integer, Hash<Integer, Integer>>]
104
+ def square_scores
105
+ @square_scores ||= build_square_scores
106
+ end
107
+
108
+ def build_square_scores # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/MethodLength,Metrics/PerceivedComplexity
109
+ rows = {}
110
+
111
+ @tiles_x_y.each do |x, y|
112
+ if x.negative? || y.negative?
113
+ raise ArgumentError,
114
+ "x and y must be greater than or equal to 0"
61
115
  end
116
+
117
+ (rows[y] ||= []) << x
118
+ end
119
+
120
+ scores = {}
121
+
122
+ next_row = nil
123
+ next_y = nil
124
+
125
+ rows.keys.sort.reverse_each do |y|
126
+ # A gap between rows means no square can continue downward.
127
+ next_row = nil unless next_y == y + 1
128
+
129
+ current_row = {}
130
+
131
+ rows[y].sort!.reverse_each do |x|
132
+ right = current_row[x + 1] || 0
133
+
134
+ if next_row
135
+ below = next_row[x] || 0
136
+ diagonal = next_row[x + 1] || 0
137
+ else
138
+ below = 0
139
+ diagonal = 0
140
+ end
141
+
142
+ # Avoid allocating [right, below, diagonal] for every tile.
143
+ min = [right, below].min
144
+ min = diagonal if diagonal < min
145
+
146
+ current_row[x] = min + 1
147
+ end
148
+
149
+ scores[y] = current_row
150
+ next_row = current_row
151
+ next_y = y
62
152
  end
63
- @tiles_lut.dig(x, y)
64
- end
65
153
 
66
- def track_result(result:, steps:, x:, y:) # rubocop:disable Naming/MethodParameterName
67
- result[:size] = steps
68
- result[:top_left_tile_x_y] << [x, y]
69
- result
154
+ scores
70
155
  end
71
156
  end
72
157
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SlippyTilesScorer
4
- VERSION = "0.0.2"
4
+ VERSION = "0.0.5"
5
5
  end
@@ -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 "./slippy_tiles_scorer/cluster"
5
- require_relative "./slippy_tiles_scorer/max_square"
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_with_two_positive_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) # rubocop:disable Naming/MethodParameterName
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:) # rubocop:disable Naming/MethodParameterName
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
 
@@ -50,16 +49,13 @@ module SlippyTilesScorer
50
49
 
51
50
  private
52
51
 
53
- def set_of_arrays?
54
- return if @tiles_x_y.all? { |point| point.is_a?(Array) && point.size == 2 }
55
-
56
- raise ArgumentError, "each point must be an array with two elements"
57
- end
58
-
59
- def set_of_arrays_of_integers?
60
- return if @tiles_x_y.all? { |point| point.all? { |coord| coord.is_a?(Integer) } }
52
+ def set_of_arrays_with_two_positive_integers?
53
+ return true if @tiles_x_y.all? do |points|
54
+ points.is_a?(Array) && points.size == 2 &&
55
+ points.all? { |point| point.is_a?(Integer) && point >= 0 }
56
+ end
61
57
 
62
- raise ArgumentError, "each point must be an array with two integers"
58
+ raise ArgumentError, "each point must be an array with two integers >= 0"
63
59
  end
64
60
  end
65
61
  end
data/mise.toml ADDED
@@ -0,0 +1,2 @@
1
+ [tools]
2
+ ruby = "jruby-10"
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,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slippy_tiles_scorer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Neutert
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-11-26 00:00:00.000000000 Z
12
- dependencies: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: set
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.1'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.1'
13
26
  description: For a given set of slippy tiles, this gem calculates the score of the
14
27
  tiles, like total tiles, clusters and max squares.
15
28
  email:
@@ -20,20 +33,24 @@ extra_rdoc_files: []
20
33
  files:
21
34
  - ".rubocop.yml"
22
35
  - CHANGELOG.md
36
+ - LICENSE
23
37
  - README.md
24
38
  - Rakefile
39
+ - Rakefile.alt-legacy
40
+ - coverage_badge.svg
25
41
  - lib/slippy_tiles_scorer.rb
26
42
  - lib/slippy_tiles_scorer/cluster.rb
27
43
  - lib/slippy_tiles_scorer/max_square.rb
28
44
  - lib/slippy_tiles_scorer/version.rb
45
+ - mise.toml
29
46
  - sig/slippy_tiles_scorer.rbs
47
+ - test_helper.rb
30
48
  homepage: https://github.com/simonneutert/slippy_tiles_scorer
31
49
  licenses: []
32
50
  metadata:
33
51
  homepage_uri: https://github.com/simonneutert/slippy_tiles_scorer
34
52
  source_code_uri: https://github.com/simonneutert/slippy_tiles_scorer
35
53
  changelog_uri: https://github.com/simonneutert/slippy_tiles_scorer/blob/main/CHANGELOG.md
36
- post_install_message:
37
54
  rdoc_options: []
38
55
  require_paths:
39
56
  - lib
@@ -41,15 +58,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
41
58
  requirements:
42
59
  - - ">="
43
60
  - !ruby/object:Gem::Version
44
- version: 3.0.0
61
+ version: 3.3.0
45
62
  required_rubygems_version: !ruby/object:Gem::Requirement
46
63
  requirements:
47
64
  - - ">="
48
65
  - !ruby/object:Gem::Version
49
66
  version: '0'
50
67
  requirements: []
51
- rubygems_version: 3.5.22
52
- signing_key:
68
+ rubygems_version: 4.0.13
53
69
  specification_version: 4
54
70
  summary: A gem to score a set of slippy tiles.
55
71
  test_files: []