sudoku_bad 0.0.2

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