slippy_tiles_scorer 0.0.2 → 0.0.3

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: ce623fd11c56fc5615043d62fd196810c7c0caccd5f9d9789dfaa864954ed2ee
4
+ data.tar.gz: 0b7296978405915b2ee53d79da2a8e8c0cc15445f2b71ce5e396ca5fc7af5a39
5
5
  SHA512:
6
- metadata.gz: f9af7b7e325f515220925b5361cb5c20aefc00add5742539d2b0dfd42a5b9f1d996b880a6d739180ac84d9a8715a68b08325ebcf34008a52a311a9cbe5b51899
7
- data.tar.gz: 85ffe608074ddf335004e48dee35feef330bea221de5fdd6f4deab9213df1d266cf29c5edf9d45d07316c588f4d3a62d19e0bd3e7689809cf328375f595a53d2
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
- '*.gemspec'
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.1.0] - 2024-11-25
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/Rakefile CHANGED
@@ -6,4 +6,7 @@ RuboCop::RakeTask.new
6
6
 
7
7
  task default: %i[test rubocop]
8
8
 
9
- # TODO: add more tasks here
9
+ desc "Run tests"
10
+ task :test do
11
+ sh "ruby test_runner.rb"
12
+ end
@@ -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) if cluster.size.positive?
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,5 +1,7 @@
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
@@ -15,43 +17,55 @@ module SlippyTilesScorer
15
17
  # @return [Hash] The size of the square and the points of the square.
16
18
  def max_squares(min_size: 3)
17
19
  @max_size_found = 0
18
- raise ArgumentError, 'min_size must be 2 or greater' if min_size < 2
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
19
27
 
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)
20
31
  result = { size: @max_size_found, top_left_tile_x_y: Set.new }
21
32
  @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?
33
+ raise ArgumentError, "x and y must be greater than or equal to 0" if x.negative? || y.negative?
23
34
 
24
35
  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)
33
- end
36
+ result = track_result(result: result, steps: steps, x: x, y: y) if steps >= min_size
34
37
  end
35
- @tiles_lut = nil
36
38
  result
37
39
  end
38
40
 
39
- def max_square(x:, y:) # rubocop:disable Naming/MethodParameterName
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:)
40
45
  steps = 1
41
46
  steps += 1 while steps_fulfilled?(x: x, y: y, steps: steps)
42
47
  steps
43
48
  end
44
49
 
45
- def steps_fulfilled?(x:, y:, steps:) # rubocop:disable Naming/MethodParameterName
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:)
46
55
  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
56
+ (0...steps).all? do |i|
57
+ in_lut?(x: x + steps, y: y + i) &&
58
+ in_lut?(x: x + i, y: y + steps)
59
+ end
51
60
  end
52
61
 
53
62
  private
54
63
 
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.
55
69
  def in_lut?(x:, y:)
56
70
  unless @tiles_lut
57
71
  @tiles_lut = {}
@@ -63,7 +77,28 @@ module SlippyTilesScorer
63
77
  @tiles_lut.dig(x, y)
64
78
  end
65
79
 
66
- def track_result(result:, steps:, x:, y:) # rubocop:disable Naming/MethodParameterName
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:)
67
102
  result[:size] = steps
68
103
  result[:top_left_tile_x_y] << [x, y]
69
104
  result
@@ -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.3"
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? && 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) # 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
 
@@ -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? { |coord| coord.is_a?(Integer) } }
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,7 +1,7 @@
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.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Neutert
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
11
  date: 2024-11-26 00:00:00.000000000 Z
12
- dependencies: []
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: