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 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
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ *swp
15
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sudoku.gemspec
4
+ gemspec
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,8 @@
1
+ #!/usr/bin/env ruby
2
+ require 'green_shoes'
3
+ require 'sudoku/main_window'
4
+
5
+ # Will start the GUI
6
+ Shoes.app(title: "My calculator", width: 200, height: 240)
7
+
8
+
@@ -0,0 +1 @@
1
+ 926548713541637289378291456815463972697152834432879561759326148283714695164985327
@@ -0,0 +1 @@
1
+ 648195723539472186217386495874931562951627834362854917793248651125769348486513279
@@ -0,0 +1 @@
1
+ 742589163596713428831624795987351642254976831163248957318462579679135284425897316
@@ -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