sudoku_bad 0.0.2
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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/Guardfile +21 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/Rakefile +11 -0
- data/bin/sudoku +8 -0
- data/data/sudoku11496.txt +1 -0
- data/data/sudoku16576.txt +1 -0
- data/data/sudoku28276.txt +1 -0
- data/data/sudoku8134.txt +1 -0
- data/lib/sudoku/grid_factory.rb +93 -0
- data/lib/sudoku/log.rb +30 -0
- data/lib/sudoku/main_window.rb +128 -0
- data/lib/sudoku/model/cell.rb +95 -0
- data/lib/sudoku/model/cell_coordinates.rb +54 -0
- data/lib/sudoku/model/coordinate.rb +61 -0
- data/lib/sudoku/model/grid.rb +98 -0
- data/lib/sudoku/solver.rb +61 -0
- data/lib/sudoku/version.rb +4 -0
- data/lib/sudoku.rb +8 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/sudoku/grid_factory_spec.rb +48 -0
- data/spec/sudoku/log_spec.rb +20 -0
- data/spec/sudoku/model/cell_coordinates_spec.rb +35 -0
- data/spec/sudoku/model/cell_spec.rb +98 -0
- data/spec/sudoku/model/coordinate_spec.rb +39 -0
- data/spec/sudoku/model/empty_grid.txt +9 -0
- data/spec/sudoku/model/grid_spec.rb +97 -0
- data/spec/sudoku/model/spec_helper.rb +3 -0
- data/spec/sudoku/solver_spec.rb +29 -0
- data/spec/sudoku/spec_helper.rb +3 -0
- data/spec/sudoku_spec.rb +12 -0
- data/sudoku.gemspec +29 -0
- metadata +203 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 60e7638a73b91eb4632f23823db6505625073b68
|
4
|
+
data.tar.gz: fede49421992f27c772754d13db856f4ed4d1691
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f23f2209b04e2c46c1535eb48d28ab56f9292715046bd889427aeb26724d7f145508f98b60756a5bcb78ef11dcbb71d1050c594fb3b4d568256f85da111e709f
|
7
|
+
data.tar.gz: 8d778460d01f879410a55cab84dbb46e7de8a34c78070a822c3c092965033890ed9f47164cef7e88caa827bdb7f6c5ca67f620d970eccfd245e66d254f881b38
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
guard :rspec, cmd: 'bundle exec rspec --color --format documentation' do
|
2
|
+
# watch /lib/ files
|
3
|
+
watch(%r{^lib/(.+).rb$}) do |m|
|
4
|
+
"spec/#{m[1]}_spec.rb"
|
5
|
+
end
|
6
|
+
|
7
|
+
# watch /lib/sudoku files
|
8
|
+
watch(%r{^lib/sudoku/(.+).rb$}) do |m|
|
9
|
+
"spec/sudoku/#{m[1]}_spec.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
# watch /lib/sudoku/model files
|
13
|
+
watch(%r{^lib/sudoku/model/(.+).rb$}) do |m|
|
14
|
+
"spec/sudoku/model/#{m[1]}_spec.rb"
|
15
|
+
end
|
16
|
+
|
17
|
+
# watch /spec/ files
|
18
|
+
watch(%r{^spec/(.+).rb$}) do |m|
|
19
|
+
"spec/#{m[1]}.rb"
|
20
|
+
end
|
21
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Tomáš Hauk
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Sudoku
|
2
|
+
|
3
|
+
This program was created as semestral work in MI-RUB at FIT CVUT
|
4
|
+
in Prague, in winter semester of 2014. Does not do anything
|
5
|
+
useful and the author recommends not using anything from this
|
6
|
+
crappy piece of software.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
Add this line to your application's Gemfile:
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
gem 'sudoku'
|
14
|
+
```
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install sudoku
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
Run the 'sudoku' program, which will start the GUI for the sudoku
|
27
|
+
game.
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it ( https://github.com/[my-github-username]/sudoku/fork )
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
|
4
|
+
# Default directory to look in is `/specs`
|
5
|
+
# Run with `rake spec`
|
6
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
7
|
+
task.rspec_opts = ['--color', '--format', 'documentation']
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :spec
|
11
|
+
|
data/bin/sudoku
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
926548713541637289378291456815463972697152834432879561759326148283714695164985327
|
@@ -0,0 +1 @@
|
|
1
|
+
648195723539472186217386495874931562951627834362854917793248651125769348486513279
|
@@ -0,0 +1 @@
|
|
1
|
+
742589163596713428831624795987351642254976831163248957318462579679135284425897316
|
data/data/sudoku8134.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
493128675568947231172653894239816457817435962654279318746381529981562743325794186
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'sudoku/model/grid'
|
2
|
+
require 'sudoku/model/cell_coordinates'
|
3
|
+
require 'sudoku/model/cell'
|
4
|
+
require 'sudoku/solver'
|
5
|
+
require 'sudoku/log'
|
6
|
+
|
7
|
+
module Sudoku
|
8
|
+
# This class handles the creation of the Grid classes.
|
9
|
+
class GridFactory
|
10
|
+
include Logging
|
11
|
+
|
12
|
+
# How many cells will be opened by this factory.
|
13
|
+
CELLS_TO_OPEN = 2
|
14
|
+
# Path to directory, where the sample grids are stored.
|
15
|
+
SAMPLES_PATH = 'data'
|
16
|
+
# Number of attempts for parsing sample sudoku file.
|
17
|
+
PARSE_ATTEMPTS = 5
|
18
|
+
|
19
|
+
# Will return path of one of the stored sudoku files in this project.
|
20
|
+
def sample_file
|
21
|
+
"#{SAMPLES_PATH}/#{Dir.entries(SAMPLES_PATH)
|
22
|
+
.select { |x| File.extname(x) == '.txt' }
|
23
|
+
.sample}"
|
24
|
+
end
|
25
|
+
|
26
|
+
# Will try to parse file on given path and will return
|
27
|
+
# initialized Grid on success. Will return nil on fail.
|
28
|
+
# * +path+ path to file to parse.
|
29
|
+
def parse_file(path)
|
30
|
+
info "Trying to parse file #{path} with sample sudoku."
|
31
|
+
File.open(path).each_with_index do |line, index|
|
32
|
+
info "Parsing line #{index}"
|
33
|
+
grid = parse_line(line)
|
34
|
+
return grid unless grid.nil?
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Will try to parse given line, where the Sudoku sample
|
40
|
+
# should be. Will return initialized instanceof Grid on
|
41
|
+
# success, nil otherwise.
|
42
|
+
# * +line+ to parse
|
43
|
+
def parse_line(line)
|
44
|
+
grid = Grid.new
|
45
|
+
generate_all_coordinates.zip(line.split('')).each do |coordinate, value|
|
46
|
+
grid.add_cell(Cell.new(coordinate, true, value.to_i))
|
47
|
+
end
|
48
|
+
grid
|
49
|
+
end
|
50
|
+
|
51
|
+
# This method will create random, simple and solvable sudoku
|
52
|
+
# grid.
|
53
|
+
def create_random
|
54
|
+
result = create_full_grid
|
55
|
+
result = create_full_grid until Sudoku::Solver.new(result).solved?
|
56
|
+
randomize_grid(result)
|
57
|
+
end
|
58
|
+
|
59
|
+
# This method will create full (solved) Sudoku grid, where
|
60
|
+
# all cells are filled and fixed.
|
61
|
+
def create_full_grid
|
62
|
+
grid = nil
|
63
|
+
(0..PARSE_ATTEMPTS).each do |i|
|
64
|
+
info "Attempt #{i}/#{PARSE_ATTEMPTS} to parse grid."
|
65
|
+
grid = parse_file(sample_file)
|
66
|
+
return grid unless grid.nil?
|
67
|
+
end
|
68
|
+
fail IOError, "Failed in creating sample Sudoku after #{i} attempts"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Will take full solved grid, and will randomly fix some
|
72
|
+
# cells and clean others.
|
73
|
+
# * +grid+ grid to randomize.
|
74
|
+
def randomize_grid(grid)
|
75
|
+
(1..CELLS_TO_OPEN).each do
|
76
|
+
cell = grid.random_cell
|
77
|
+
cell = grid.random_cell until cell.fixed
|
78
|
+
grid.add_cell(Cell.new(cell.coordinates, false, 0))
|
79
|
+
end
|
80
|
+
grid
|
81
|
+
end
|
82
|
+
|
83
|
+
# Will generate all possible coordinates, which can exist in
|
84
|
+
# the sudoku grid.
|
85
|
+
def generate_all_coordinates
|
86
|
+
result = []
|
87
|
+
(0..8).to_a.repeated_permutation(2).to_a.each do
|
88
|
+
|x| result << Sudoku::CellCoordinates.new(x[0], x[1])
|
89
|
+
end
|
90
|
+
result.sort
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/sudoku/log.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Sudoku
|
5
|
+
# Module for logging functionality
|
6
|
+
module Logging
|
7
|
+
# Logger for use in the Sudoku classees
|
8
|
+
LOGGER = Logger.new(STDOUT)
|
9
|
+
|
10
|
+
# Will log info message
|
11
|
+
def info(message)
|
12
|
+
LOGGER.info(message)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Will log error message
|
16
|
+
def error(message)
|
17
|
+
LOGGER.error(message)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Will log fatal message
|
21
|
+
def fatal(message)
|
22
|
+
LOGGER.error(message)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Will disable all logging except for fatal errors.
|
26
|
+
def disable_log
|
27
|
+
LOGGER.level = Logger::FATAL
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'green_shoes'
|
2
|
+
require 'sudoku/grid_factory'
|
3
|
+
require 'sudoku/log'
|
4
|
+
|
5
|
+
module Sudoku
|
6
|
+
# This class represents main window for the Sudoku GUI.
|
7
|
+
class MainWindow < Shoes
|
8
|
+
include Logging
|
9
|
+
url '/', :index
|
10
|
+
|
11
|
+
# Will initialize the model
|
12
|
+
def init_model
|
13
|
+
info 'Initializing model'
|
14
|
+
@grid = Sudoku::GridFactory.new.create_random
|
15
|
+
info "Generated grid\n#{@grid}"
|
16
|
+
end
|
17
|
+
|
18
|
+
# This will create new sudoku and will render main page
|
19
|
+
def index
|
20
|
+
info 'Rendering GUI'
|
21
|
+
init_model
|
22
|
+
flow width: 1.0, height: 0.8 do
|
23
|
+
render_grid
|
24
|
+
end
|
25
|
+
flow width: 1.0, height: 0.2 do
|
26
|
+
render_help
|
27
|
+
end
|
28
|
+
info 'GUI rendered'
|
29
|
+
end
|
30
|
+
|
31
|
+
# Will render basic help text
|
32
|
+
def render_help
|
33
|
+
caption 'Help'
|
34
|
+
para ' - Left click on the cell will select and increment the cell.'
|
35
|
+
para ' - Right click on the cell will select and decrement the cell.'
|
36
|
+
para ' - When cell is selected, you can use number keys to fill it.'
|
37
|
+
end
|
38
|
+
|
39
|
+
# This will render the grid for sudoku.
|
40
|
+
def render_grid
|
41
|
+
info 'Rendering grid'
|
42
|
+
flow width: 700, heiht: 700 do
|
43
|
+
@grid.cells.values.sort.each { |x| render_cell(x) }
|
44
|
+
end
|
45
|
+
keypress { |key| handle_key_press(key) }
|
46
|
+
info 'Grid rendered.'
|
47
|
+
end
|
48
|
+
|
49
|
+
# If there is some selected cell and the number is pressed,
|
50
|
+
# the number will be used as input to the cell.
|
51
|
+
def handle_key_press(key)
|
52
|
+
info "Pressed key #{key}"
|
53
|
+
return if @selected.nil? || !('0'..'9').include?(key)
|
54
|
+
flow, cell = @selected
|
55
|
+
cell.value = key.to_i
|
56
|
+
flow.clear { fill_cell(cell) }
|
57
|
+
info "Value of cell #{cell.coordinates} set to #{cell.value}"
|
58
|
+
handle_end_game
|
59
|
+
end
|
60
|
+
|
61
|
+
# Will render particular cell.
|
62
|
+
def render_cell(cell)
|
63
|
+
cell_flow = flow width: 0.1111 do
|
64
|
+
fill_cell(cell)
|
65
|
+
end
|
66
|
+
cell_flow.click { |button| handle_cell_click(cell, cell_flow, button) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Will handle click on the cell
|
70
|
+
def handle_cell_click(cell, flow, button)
|
71
|
+
info "Detected click on the cell #{cell}"
|
72
|
+
cell.increment if button == 1
|
73
|
+
cell.decrement if button == 3
|
74
|
+
reset_selected_flow(flow, cell)
|
75
|
+
flow.clear { fill_cell(cell) }
|
76
|
+
handle_end_game
|
77
|
+
end
|
78
|
+
|
79
|
+
# Will reset selected flow and will set the selected to the
|
80
|
+
# new values.
|
81
|
+
# * +new_flow+ new selected flow
|
82
|
+
# * +new_cell+ new selected cell
|
83
|
+
def reset_selected_flow(new_flow, new_cell)
|
84
|
+
return @selected if new_cell.fixed
|
85
|
+
return @selected = [new_flow, new_cell] if @selected.nil?
|
86
|
+
old_flow, old_cell = @selected
|
87
|
+
@selected = [new_flow, new_cell]
|
88
|
+
old_flow.clear { fill_cell(old_cell) }
|
89
|
+
end
|
90
|
+
|
91
|
+
# Will fill the given container with cell content.
|
92
|
+
def fill_cell(cell)
|
93
|
+
background cell_background(cell)
|
94
|
+
strokewidth get_stroke_width(cell)
|
95
|
+
border get_cell_border(cell)
|
96
|
+
banner cell.as_string, align: 'center' if cell.fixed
|
97
|
+
banner cell.as_string, align: 'center', weight: 'bold' unless cell.fixed
|
98
|
+
end
|
99
|
+
|
100
|
+
# Will determine border of the cell.
|
101
|
+
def get_cell_border(cell)
|
102
|
+
return black if @selected.nil? || @selected[1] != cell
|
103
|
+
red
|
104
|
+
end
|
105
|
+
|
106
|
+
def get_stroke_width(cell)
|
107
|
+
return 1 if @selected.nil? || @selected[1] != cell
|
108
|
+
2
|
109
|
+
end
|
110
|
+
|
111
|
+
# Will determine and return background for cell
|
112
|
+
def cell_background(cell)
|
113
|
+
color1 = rgb(108, 140, 236)
|
114
|
+
color2 = rgb(213, 152, 108)
|
115
|
+
x, y = cell.coordinates.coordinate_x, cell.coordinates.coordinate_y
|
116
|
+
if x.coordinate.between?(3, 5)
|
117
|
+
y.coordinate.between?(3, 5) ? (return color1) : (return color2)
|
118
|
+
else
|
119
|
+
y.coordinate.between?(3, 5) ? (return color2) : (return color1)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Will check, if the grid is solved. If so, will show alert.
|
124
|
+
def handle_end_game
|
125
|
+
alert 'Grid successfully solved!' if @grid.solved?
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Sudoku
|
2
|
+
# Represents single Cell in the Sudoku grid.
|
3
|
+
class Cell
|
4
|
+
include Comparable
|
5
|
+
attr_accessor :value, :fixed, :coordinates
|
6
|
+
|
7
|
+
# Initializes this cell. It is not possible to initialize
|
8
|
+
# fixed cell with value 0.
|
9
|
+
# * +fixed+ boolean value, which indicates if this cell is fixed
|
10
|
+
# * +coordinates+ X Y coordinates of this cell.
|
11
|
+
# * +value+ numerical value of this cell. Default set to 0.
|
12
|
+
def initialize(coordinates, fixed = false, value = 0)
|
13
|
+
fail ArgumentError,
|
14
|
+
'Cant initialize fixed cell with value 0' if fixed && value == 0
|
15
|
+
|
16
|
+
fail ArgumentError,
|
17
|
+
'Wrong coordinate parameter.' unless
|
18
|
+
coordinates.respond_to?(:coordinate_x) &&
|
19
|
+
coordinates.respond_to?(:coordinate_y)
|
20
|
+
|
21
|
+
self.value = value
|
22
|
+
@fixed = fixed
|
23
|
+
@coordinates = CellCoordinates.new(coordinates.coordinate_x,
|
24
|
+
coordinates.coordinate_y)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Will return value of this cell in the correct string
|
28
|
+
# format. As an empty string, if the value is 0, or as value
|
29
|
+
# string represetation.
|
30
|
+
def as_string
|
31
|
+
return '' unless @value != 0
|
32
|
+
@value.to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
# Will increment cell value, if the cell is not fixed. Will
|
36
|
+
# reset on overflow. Returns new value.
|
37
|
+
def increment
|
38
|
+
return @value if @fixed
|
39
|
+
@value += 1
|
40
|
+
@value = 0 if @value > 9
|
41
|
+
@value
|
42
|
+
end
|
43
|
+
|
44
|
+
# Will return true, if coordinates of this cell belongs to
|
45
|
+
# area given by two coordinate ranges.
|
46
|
+
# * +x+ range of x coordinates
|
47
|
+
# * +y+ range of y coordinates
|
48
|
+
def in_area(x, y)
|
49
|
+
@coordinates.in_area(x, y)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Will decrement cell value, if the cell is not fixed. Will
|
53
|
+
# reset on underflow. Returns new value.
|
54
|
+
def decrement
|
55
|
+
return @value if @fixed
|
56
|
+
@value -= 1
|
57
|
+
@value = 9 if @value < 0
|
58
|
+
@value
|
59
|
+
end
|
60
|
+
|
61
|
+
# The cells are equal, if they have the same coordinate, no
|
62
|
+
# matter the value and fixed tag.
|
63
|
+
def eql?(other)
|
64
|
+
return false unless other.respond_to?(:coordinates)
|
65
|
+
@coordinates.eql?(other.coordinates)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Same as eql?
|
69
|
+
def ==(other)
|
70
|
+
eql?(other)
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_s
|
74
|
+
"#{@coordinates}, #{@value}, #{fixed}"
|
75
|
+
end
|
76
|
+
|
77
|
+
def <=>(other)
|
78
|
+
@coordinates <=> other.coordinates
|
79
|
+
end
|
80
|
+
|
81
|
+
# Will set the value of this cell. New value must be in the
|
82
|
+
# interval of 0 - 9, where 0 means, that the cell is not
|
83
|
+
# filled yet. This method will fail, if the attribute fixed
|
84
|
+
# is set to true.
|
85
|
+
# * +value+ new value to set to this cell
|
86
|
+
def value=(value)
|
87
|
+
fail ArgumentError,
|
88
|
+
'Cant assign new value to fixed value cell.' if @fixed
|
89
|
+
fail ArgumentError,
|
90
|
+
'Cell initialization value out of interval 0-9' if
|
91
|
+
value < 0 || value > 9
|
92
|
+
@value = value
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'sudoku/model/coordinate'
|
2
|
+
|
3
|
+
module Sudoku
|
4
|
+
# Represents a two dimensional coordinates in the Sudoku grid.
|
5
|
+
class CellCoordinates
|
6
|
+
include Comparable
|
7
|
+
attr_reader :coordinate_x, :coordinate_y
|
8
|
+
|
9
|
+
# Will initialize this cell coordinate.
|
10
|
+
# * +x+ horizontal coordinate.
|
11
|
+
# * +y+ vertical coordinate.
|
12
|
+
def initialize(x, y)
|
13
|
+
@coordinate_x = Sudoku::Coordinate.new(x)
|
14
|
+
@coordinate_y = Sudoku::Coordinate.new(y)
|
15
|
+
end
|
16
|
+
|
17
|
+
def ==(other)
|
18
|
+
self.class == other.class &&
|
19
|
+
@coordinate_x == other.coordinate_x &&
|
20
|
+
@coordinate_y == other.coordinate_y
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method :eql?, :==
|
24
|
+
|
25
|
+
def hash
|
26
|
+
@coordinate_x.coordinate ^ @coordinate_y.coordinate # XOR, from the doc
|
27
|
+
end
|
28
|
+
|
29
|
+
def <=>(other)
|
30
|
+
return @coordinate_y <=> other.coordinate_y if
|
31
|
+
@coordinate_y != other.coordinate_y
|
32
|
+
@coordinate_x <=> other.coordinate_x
|
33
|
+
end
|
34
|
+
|
35
|
+
# Will return this coordinate in the format [x, y]
|
36
|
+
def to_s
|
37
|
+
"[#{coordinate_x}, #{coordinate_y}]"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Will return true, if this coordinate belongs to area given
|
41
|
+
# by two ranges of coordinates.
|
42
|
+
# * +x+ range of x coordinates to accept.
|
43
|
+
# * +y+ range of y coordinates to accept.
|
44
|
+
def in_area(x, y)
|
45
|
+
return true if x.include?(@coordinate_x) && y.include?(@coordinate_y)
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# Will return randomly generated coordinate.
|
50
|
+
def self.random
|
51
|
+
CellCoordinates.new(Random.rand(0..8), Random.rand(0..8))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Sudoku
|
2
|
+
# Represents a cell coordinate in one dimension of the Sudoku
|
3
|
+
# grid.
|
4
|
+
class Coordinate
|
5
|
+
include Comparable
|
6
|
+
|
7
|
+
attr_reader :coordinate
|
8
|
+
|
9
|
+
# Will initialize this coordinate.
|
10
|
+
# * +coordinate+ coordinate value, which must be in the 0-8 interval.
|
11
|
+
def initialize(coordinate)
|
12
|
+
fail ArgumentError,
|
13
|
+
"Coordinate value must be 0-8, value #{coordinate} given." unless
|
14
|
+
coordinate_valid?(coordinate)
|
15
|
+
|
16
|
+
@coordinate = coordinate.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
# Will check if given number can be used as the valid coordinate,
|
20
|
+
# which means, that it is a number in the interval
|
21
|
+
# 0-8 inclusive. True is returned if the coordinate is valid,
|
22
|
+
# false otherwise.
|
23
|
+
# * +coordinate+ coordinate value to check
|
24
|
+
def coordinate_valid?(coordinate)
|
25
|
+
Coordinate.valid?(coordinate)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Will return true, if the given coordinate is between 0-8
|
29
|
+
# interval, inclusive.
|
30
|
+
def self.valid?(coordinate)
|
31
|
+
return true if coordinate >= 0 && coordinate < 9
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_s
|
36
|
+
@coordinate.to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
# Will convert this coordinate to integer.
|
40
|
+
def to_i
|
41
|
+
@coordinate
|
42
|
+
end
|
43
|
+
|
44
|
+
# Will convert this coordinate to integer.
|
45
|
+
def to_int
|
46
|
+
to_i
|
47
|
+
end
|
48
|
+
|
49
|
+
# Compares this cooridnate with the other one.
|
50
|
+
def <=>(other)
|
51
|
+
return @coordinate <=> other.coordinate if other.respond_to?(:coordinate)
|
52
|
+
@coordinate <=> other.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
# Will check, if the given element represents the same
|
56
|
+
# one-dimensional coordiante.
|
57
|
+
def eql?(other)
|
58
|
+
@coordinate == other.to_i
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|