namero 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d21908e8458c2569f1584e9e3f8b55ffd5907abb713ede832d1432d2ffd6df2
4
- data.tar.gz: 11d343b7e82c3bc8f679e8f60bf0eaed1986ea045b8681791da495b2d15bc459
3
+ metadata.gz: 027c27f9e7b9a2f8e9662c961c907c4cd38c1c4f6d5461bbd4ad03673604b605
4
+ data.tar.gz: 31fe4354d07c60114a714bc33a36f3800984d76ec66f78566d2bebd3d8b80dc0
5
5
  SHA512:
6
- metadata.gz: 6c6353da77e5906617a5fdbe110490ec4006d1cb1ab1bd171f6ff5ab06a9ddd2d2cfbc4d21dab3c7a1000b5336c0f11512bbb6da4ed68f4be225e9f37ceb803b
7
- data.tar.gz: 64caf28995793c5a853d90f4a4ade297a761fc5b3ce10813ae3d1b215fe11e30b6e403b9cb02c93689cc9a09eb6d16cbe0ce1d9142841c37e5053c1522d3bf11
6
+ metadata.gz: 478c5e53b2d38163b353cf97ae127bdc70699dc00f3010c7be1dfcb07f70479fed3ef8d0bd468c7c1d2e1ebcc772e4cd0bed853bcfd01876c10958b4a9ff228a
7
+ data.tar.gz: 2846d3036c4be2449d30d39c0ecc2a206dea9a67ee3483a8457950b8bbdb1b65958d8fce579061bd38c776f6ef5dc7b0a05dacf8ea37249b9a3ad5347bb2a64e
@@ -1,6 +1,7 @@
1
1
  require "namero/version"
2
2
  require 'namero/board'
3
3
  require 'namero/solver'
4
+ require 'namero/solver_extensions'
4
5
  require 'namero/value'
5
6
 
6
7
  module Namero
@@ -2,46 +2,60 @@ module Namero
2
2
  class Board
3
3
  attr_reader :n
4
4
 
5
+ # ..4.
6
+ # ...1
7
+ # 4...
8
+ # 21..
9
+ def self.load_from_string(str, n)
10
+ values = str.split("\n").map(&:chars).flatten.map.with_index do |v, idx|
11
+ if v == '.'
12
+ Namero::Value.new(value: nil, candidates: (1..n).to_a, index: idx)
13
+ else
14
+ Namero::Value.new(value: Integer(v), candidates: [Integer(v)], index: idx)
15
+ end
16
+ end
17
+ Namero::Board.new(n: n, values: values)
18
+ end
19
+
20
+ # [
21
+ # nil, nil, 4, nil,
22
+ # nil, nil, nil, 1,
23
+ # 4, nil, nil, nil,
24
+ # 2, 1, nil, nil,
25
+ # ]
26
+ def self.load_from_array(array, n)
27
+ values = array.map.with_index do |v, idx|
28
+ if v
29
+ Namero::Value.new(value: v, candidates: [v], index: idx)
30
+ else
31
+ Namero::Value.new(value: nil, candidates: (1..n).to_a, index: idx)
32
+ end
33
+ end
34
+ Namero::Board.new(n: n, values: values)
35
+ end
36
+
5
37
  # n: Integer
6
38
  # values: Array<Integer>
7
39
  def initialize(n:, values: Array.new(n**2))
8
40
  raise ArgumentError if values.size != n**2
9
41
  @n = n
10
42
  @values = values
11
- end
12
-
13
- # x, y: 0 based index
14
- # type: :single, :row, :column, :block, :index
15
- def []=(idx, type = :index, value)
16
- case type
17
- when :index
18
- @values[idx] = value
19
- when :row
20
- start = (idx / n) * n
21
- n.times do |i|
22
- @values[start+i] = value[i]
23
- end
24
- when :column
25
- x = idx % n
26
- n.times do |i|
27
- @values[i * n + x] = value[i]
28
- end
29
- when :block
30
- x = idx % n
31
- y = idx / n
32
- start_x = x / root_n * root_n
33
- start_y = y / root_n * root_n
34
-
35
- value_idx = 0
43
+ @rows = Array.new(n) do |i|
44
+ start = i * n
45
+ values[start...start+n]
46
+ end
47
+ @columns = Array.new(n) do |x|
48
+ Array.new(n) { |i| values[i * n + x] }
49
+ end
50
+ @blocks = Array.new(n) do |i|
51
+ idx = i % root_n * root_n + (i / root_n) * n * root_n
52
+ block = []
36
53
  root_n.times do |y_offset|
37
54
  root_n.times do |x_offset|
38
-
39
- self[start_x + x_offset + (start_y + y_offset) * n] = value[value_idx]
40
- value_idx += 1
55
+ block << values[idx + x_offset + y_offset * n]
41
56
  end
42
57
  end
43
- else
44
- raise "Unknown type: #{type}"
58
+ block
45
59
  end
46
60
  end
47
61
 
@@ -50,25 +64,12 @@ module Namero
50
64
  when :index
51
65
  @values[idx]
52
66
  when :row
53
- start = (idx / n ) * n
54
- @values[start...start+@n]
67
+ @rows[idx / n]
55
68
  when :column
56
69
  x = idx % n
57
- @values.each_slice(n).map{|row| row[x]}
70
+ @columns[x]
58
71
  when :block
59
- x = idx % n
60
- y = idx / n
61
- start_x = x / root_n * root_n
62
- start_y = y / root_n * root_n
63
-
64
- [].tap do |res|
65
- root_n.times do |y_offset|
66
- root_n.times do |x_offset|
67
-
68
- res << self[start_x + x_offset + (start_y + y_offset) * n]
69
- end
70
- end
71
- end
72
+ @blocks[idx / n / root_n * root_n + idx % n / root_n]
72
73
  else
73
74
  raise "Unknown type: #{type}"
74
75
  end
@@ -86,15 +87,89 @@ module Namero
86
87
  end
87
88
  end
88
89
 
90
+ def each_affected_group(&block)
91
+ return enum_for(__method__) unless block_given?
92
+
93
+ @rows.each(&block)
94
+ @columns.each(&block)
95
+ @blocks.each(&block)
96
+ end
97
+
89
98
  def complete?
90
- @values.all? do |v|
99
+ all_filled = @values.all? do |v|
91
100
  v.value
92
101
  end
102
+ return false unless all_filled
103
+
104
+ return each_affected_group.all? do |group|
105
+ group.map(&:value).uniq.size == n
106
+ end
93
107
  end
94
108
 
95
- private
109
+ def to_s(mode: :simple)
110
+ out = +""
111
+ case mode
112
+ when :simple
113
+ @values.each.with_index do |v, idx|
114
+ if v.value
115
+ out << (v.value).to_s
116
+ else
117
+ out << '.'
118
+ end
119
+ out << "\n" if idx % n == n - 1
120
+ end
121
+ when :with_candidates
122
+ @values.each.each_slice(n).with_index do |values, index|
123
+ values.each_slice(root_n) do |values2|
124
+ values2.each do |v|
125
+ if v.value
126
+ out << '***'
127
+ else
128
+ out << (v.candidates.include?(1) ? '1' : '.')
129
+ out << (v.candidates.include?(2) ? '2' : '.')
130
+ out << (v.candidates.include?(3) ? '3' : '.')
131
+ end
132
+ out << ' '
133
+ end
134
+ out << ' | '
135
+ end
136
+ out << "\n"
137
+ values.each_slice(root_n) do |values2|
138
+ values2.each do |v|
139
+ if v.value
140
+ out << "*#{v.value}*"
141
+ else
142
+ out << (v.candidates.include?(4) ? '4' : '.')
143
+ out << (v.candidates.include?(5) ? '5' : '.')
144
+ out << (v.candidates.include?(6) ? '6' : '.')
145
+ end
146
+ out << ' '
147
+ end
148
+ out << ' | '
149
+ end
150
+ out << "\n"
151
+ values.each_slice(root_n) do |values2|
152
+ values2.each do |v|
153
+ if v.value
154
+ out << '***'
155
+ else
156
+ out << (v.candidates.include?(7) ? '7' : '.')
157
+ out << (v.candidates.include?(8) ? '8' : '.')
158
+ out << (v.candidates.include?(9) ? '9' : '.')
159
+ end
160
+ out << ' '
161
+ end
162
+ out << ' | '
163
+ end
164
+ out << "\n#{'-' * ((root_n + 1) * n + (root_n) * 3) if index % root_n == root_n - 1}\n"
165
+ end
166
+ else
167
+ raise "unknown mode: #{mode}"
168
+ end
169
+ out
170
+ end
96
171
 
97
- def root_n
172
+ private def root_n
98
173
  Integer.sqrt(n)
99
174
  end
100
175
  end
@@ -2,25 +2,28 @@ require 'set'
2
2
 
3
3
  module Namero
4
4
  class Solver
5
- def initialize(board)
5
+ def initialize(board, extensions: [])
6
6
  @board = board
7
7
  @updated_candidate_queue = Set.new
8
+ @extensions = extensions
8
9
  end
9
10
 
10
11
  def solve
11
12
  fill_candidates
12
13
  loop do
13
- idx = @updated_candidate_queue.each.first
14
- break unless idx
14
+ idx = updated_candidate_queue.first
15
+ unless idx
16
+ extensions.each do |ex|
17
+ ex.solve(board, updated_candidate_queue)
18
+ end
19
+ break if updated_candidate_queue.empty?
20
+ next
21
+ end
15
22
  @updated_candidate_queue.delete idx
16
23
  fill_one_candidate(idx)
17
24
  end
18
25
  end
19
26
 
20
- private
21
-
22
- attr_reader :board, :updated_candidate_queue
23
-
24
27
  def fill_one_candidate(idx)
25
28
  v = board[idx]
26
29
  return if v.candidates.size != 1
@@ -28,23 +31,27 @@ module Namero
28
31
  fill_candidate_for(v)
29
32
  end
30
33
 
34
+ private
35
+
36
+ attr_reader :board, :updated_candidate_queue, :extensions
37
+
31
38
  def fill_candidate_for(v)
32
39
  board[v.index, :row].each do |v2|
33
40
  unless v2.value
34
- v2.candidates -= [v.value]
35
- updated_candidate_queue << v2.index
41
+ changed = v2.candidates.delete(v.value)
42
+ updated_candidate_queue << v2.index if changed
36
43
  end
37
44
  end
38
45
  board[v.index, :column].each do |v2|
39
46
  unless v2.value
40
- v2.candidates -= [v.value]
41
- updated_candidate_queue << v2.index
47
+ changed = v2.candidates.delete(v.value)
48
+ updated_candidate_queue << v2.index if changed
42
49
  end
43
50
  end
44
51
  board[v.index, :block].each do |v2|
45
52
  unless v2.value
46
- v2.candidates -= [v.value]
47
- updated_candidate_queue << v2.index
53
+ changed = v2.candidates.delete(v.value)
54
+ updated_candidate_queue << v2.index if changed
48
55
  end
49
56
  end
50
57
  v.candidates = [v.value]
@@ -0,0 +1,50 @@
1
+ module Namero
2
+ module SolverExtensions
3
+ # If a candidate number exists only one place in an affected group,
4
+ # we can fill the box.
5
+ # For example:
6
+ # When candidates are: [1 2 3] [2 3] [1 2 3] [1 2 3 4]
7
+ # The last box's value is 4 because there is 4 candidate only the last box.
8
+ class CandidateExistsOnlyOnePlace
9
+ def self.solve(board, queue)
10
+ self.new(board, queue).solve
11
+ end
12
+
13
+ def initialize(board, queue)
14
+ @board = board
15
+ @queue = queue
16
+ end
17
+
18
+ def solve
19
+ board.each_affected_group do |group|
20
+ table = []
21
+ group.each do |value|
22
+ next if value.value
23
+ value.candidates.each do |c|
24
+ if table[c].nil?
25
+ table[c] = value
26
+ elsif table[c]
27
+ table[c] = false
28
+ end
29
+ end
30
+ end
31
+
32
+ table.each.with_index do |value, idx|
33
+ if value
34
+ value.candidates = [idx]
35
+ queue << value.index
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :board, :queue
44
+
45
+ def n
46
+ board.n
47
+ end
48
+ end
49
+ end
50
+ end
@@ -10,7 +10,7 @@ module Namero
10
10
  end
11
11
 
12
12
  def dup
13
- self.new(value: value, candidates: candidates.dup)
13
+ self.class.new(value: value, candidates: candidates.dup, index: index)
14
14
  end
15
15
  end
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module Namero
2
- VERSION = "0.0.1"
2
+ VERSION = "1.0.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: namero
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masataka Pocke Kuwabara
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-11-17 00:00:00.000000000 Z
11
+ date: 2020-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -68,6 +68,7 @@ files:
68
68
  - lib/namero.rb
69
69
  - lib/namero/board.rb
70
70
  - lib/namero/solver.rb
71
+ - lib/namero/solver_extensions.rb
71
72
  - lib/namero/value.rb
72
73
  - lib/namero/version.rb
73
74
  - namero.gemspec
@@ -89,8 +90,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
90
  - !ruby/object:Gem::Version
90
91
  version: '0'
91
92
  requirements: []
92
- rubyforge_project:
93
- rubygems_version: 2.7.6
93
+ rubygems_version: 3.2.0.pre1
94
94
  signing_key:
95
95
  specification_version: 4
96
96
  summary: ''