sudoku_bad 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|