binary_puzzle_solver 0.0.3 → 0.0.4

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.
@@ -3,6 +3,10 @@
3
3
  require 'binary_puzzle_solver'
4
4
 
5
5
  filename = ARGV.shift
6
+ if (not filename)
7
+ raise "Missing path to the file containing a layout."
8
+ end
9
+
6
10
  board = Binary_Puzzle_Solver.gen_board_from_string_v1(IO.read(filename))
7
11
 
8
12
  board.add_to_iters_quota(1_000_000_000);
@@ -13,6 +17,7 @@ board.try_to_solve_using(
13
17
  :check_and_handle_cells_of_one_value_in_row_were_all_found,
14
18
  :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
15
19
  :check_try_placing_last_of_certain_digit_in_row,
20
+ :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
16
21
  ]
17
22
  );
18
23
 
@@ -80,15 +80,19 @@ module Binary_Puzzle_Solver
80
80
  return
81
81
  end
82
82
 
83
- def get_char()
84
- if state == ZERO
83
+ def self.get_state_char(val)
84
+ if val == ZERO
85
85
  return '0'
86
- elsif state == ONE
86
+ elsif val == ONE
87
87
  return '1'
88
88
  else
89
- raise RuntimeError, "get_char() called on Unset state"
89
+ raise RuntimeError, "get_state_char() called on Unset state"
90
90
  end
91
91
  end
92
+
93
+ def get_char()
94
+ return Cell.get_state_char(state)
95
+ end
92
96
  end
93
97
 
94
98
  # A summary for a row or column.
@@ -165,6 +169,7 @@ module Binary_Puzzle_Solver
165
169
  class Board
166
170
 
167
171
  attr_reader :iters_quota, :num_iters_done
172
+
168
173
  def initialize (params)
169
174
  @dim_limits = {:x => params[:x], :y => params[:y]}
170
175
  @cells = dim_range(:y).map {
@@ -174,6 +179,10 @@ module Binary_Puzzle_Solver
174
179
  :x => dim_range(:x).map { RowSummary.new(limit(:y)); },
175
180
  :y => dim_range(:y).map { RowSummary.new(limit(:x)); }
176
181
  }
182
+ @complete_rows_map = {
183
+ :x => Hash.new,
184
+ :y => Hash.new
185
+ }
177
186
  @old_moves = []
178
187
  @new_moves = []
179
188
 
@@ -258,10 +267,37 @@ module Binary_Puzzle_Solver
258
267
  return @cells[y][x]
259
268
  end
260
269
 
270
+ def list_views
271
+ return [false, true].map { |v| get_view(:rotate => v) }
272
+ end
273
+
274
+ def get_complete_map(dir)
275
+ return @complete_rows_map[dir]
276
+ end
277
+
261
278
  def set_cell_state(coord, state)
262
279
  _get_cell(coord).set_state(state)
263
- get_row_summary(:dim => :x, :idx => coord.x).inc_count(state)
264
- get_row_summary(:dim => :y, :idx => coord.y).inc_count(state)
280
+
281
+ mymap = @complete_rows_map
282
+ list_views().each do |v|
283
+ row_dim = v.row_dim()
284
+ real_row_dim = v.mapped_row_dim()
285
+ row_idx = coord.method(real_row_dim).call()
286
+ summary = v.get_row_summary(:dim => row_dim, :idx => row_idx)
287
+
288
+ # puts "Coord = (x=#{coord.x},y=#{coord.y}) row_dim = #{row_dim} RowIdx = #{row_idx} mapped_row_dim = #{v.mapped_row_dim}"
289
+ summary.inc_count(state)
290
+
291
+ if summary.are_both_full() then
292
+ str = v.get_row_handle(row_idx).get_string()
293
+ mymap[real_row_dim][str] ||= []
294
+ arr = mymap[real_row_dim][str]
295
+ arr << row_idx
296
+ if (arr.length > 1) then
297
+ raise GameIntegrityException, "Duplicate rows at dim #{row_dim} #{arr.join(',')}"
298
+ end
299
+ end
300
+ end
265
301
  return
266
302
  end
267
303
 
@@ -300,7 +336,7 @@ module Binary_Puzzle_Solver
300
336
 
301
337
  def try_to_solve_using (params)
302
338
  methods_list = params[:methods]
303
- views = [get_view(:rotate => false), get_view(:rotate => true), ]
339
+ views = list_views()
304
340
 
305
341
  catch :out_of_iters do
306
342
  first_iter = true
@@ -349,11 +385,9 @@ module Binary_Puzzle_Solver
349
385
  end
350
386
 
351
387
  def validate()
352
- views = [get_view(:rotate => false), get_view(:rotate => true), ]
353
-
354
388
  is_final = true
355
389
 
356
- views.each do |v|
390
+ list_views().each do |v|
357
391
  view_final = v.validate_rows()
358
392
  is_final &&= view_final
359
393
  end
@@ -423,6 +457,14 @@ module Binary_Puzzle_Solver
423
457
  return :x
424
458
  end
425
459
 
460
+ def mapped_row_dim()
461
+ return _calc_mapped_dir(row_dim())
462
+ end
463
+
464
+ def get_complete_row_map()
465
+ return @board.get_complete_map(mapped_row_dim())
466
+ end
467
+
426
468
  def get_row_handle(idx)
427
469
  return RowHandle.new(self, idx)
428
470
  end
@@ -444,8 +486,14 @@ module Binary_Puzzle_Solver
444
486
  return
445
487
  end
446
488
 
489
+ def set_cell_state(coord, state)
490
+ @board.set_cell_state(
491
+ _calc_mapped_coord(coord), state
492
+ )
493
+ end
494
+
447
495
  def perform_and_append_move(params)
448
- set_cell_state(params[:coord], params[:val])
496
+ set_cell_state( params[:coord], params[:val] )
449
497
  _append_move(params)
450
498
  end
451
499
 
@@ -647,9 +695,55 @@ module Binary_Puzzle_Solver
647
695
  end
648
696
  end
649
697
 
698
+ def _do_values_have_a_three_in_a_row(params)
699
+ row_idx = params[:idx]
700
+ v_s = params[:v_s]
701
+
702
+ return {
703
+ :verdict => ((0 .. (v_s.length - 3)).to_a.index { |i|
704
+ (1 .. 2).all? { |offset| v_s[i] == v_s[i+offset] }
705
+ }),
706
+ :reason => "Trying opposite value that is the last remaining results in three-in-a-row",
707
+ }
708
+ end
709
+
710
+ def _are_values_duplicates(params)
711
+ row_idx = params[:idx]
712
+ v_s = params[:v_s]
713
+
714
+ str = RowHandle.calc_iter_of_states_str(v_s)
715
+ mymap = get_complete_row_map()
716
+
717
+ verdict = (mymap.has_key?(str) and (mymap[str].length > 0));
718
+
719
+ return {
720
+ :verdict => verdict,
721
+ :reason => "Trying opposite value that is the last remaining results in a duplicate row",
722
+ }
723
+ end
724
+
725
+ def check_try_placing_last_of_certain_digit_in_row_to_avoid_dups(params)
726
+ row_idx = params[:idx]
727
+
728
+ return _generic_check_try_placing_last_digit(
729
+ :idx => row_idx,
730
+ :callback_method => :_are_values_duplicates,
731
+ )
732
+ end
733
+
650
734
  def check_try_placing_last_of_certain_digit_in_row(params)
651
735
  row_idx = params[:idx]
652
736
 
737
+ return _generic_check_try_placing_last_digit(
738
+ :idx => row_idx,
739
+ :callback_method => :_do_values_have_a_three_in_a_row,
740
+ )
741
+ end
742
+
743
+ def _generic_check_try_placing_last_digit(params)
744
+ row_idx = params[:idx]
745
+ callback_method = params[:callback_method]
746
+
653
747
  row = get_row_handle(row_idx)
654
748
 
655
749
  summary = row.get_summary()
@@ -676,16 +770,18 @@ module Binary_Puzzle_Solver
676
770
  end
677
771
 
678
772
  # Is there a three in a row?
679
- if ((0 .. (v_s.length - 3)).to_a.index { |i|
680
- (1 .. 2).all? { |offset| v_s[i] == v_s[i+offset] }
681
- }) then
682
- perform_and_append_move(
683
- :coord => row.get_coord(x),
684
- :val => oppose_v,
685
- :reason => "Trying opposite value that is the last remaining results in three-in-a-row",
686
- :dir => col_dim()
773
+ result = method(callback_method).call(
774
+ :idx => row_idx, :v_s => v_s
687
775
  )
688
- return
776
+
777
+ if (result[:verdict])
778
+ perform_and_append_move(
779
+ :coord => row.get_coord(x),
780
+ :val => oppose_v,
781
+ :reason => result[:reason],
782
+ :dir => col_dim()
783
+ )
784
+ return
689
785
  end
690
786
  end
691
787
 
@@ -693,19 +789,18 @@ module Binary_Puzzle_Solver
693
789
  end
694
790
 
695
791
  def validate_rows()
696
- # TODO
697
- complete_rows_map = Hash.new
698
-
699
792
  is_final = true
700
793
 
701
794
  dim_range(row_dim()).each do |row_idx|
702
795
  row = get_row_handle(row_idx)
703
- ret = row.validate( :complete_rows_map => complete_rows_map )
796
+ ret = row.validate(:foo => false)
704
797
  is_final &&= ret[:is_final]
705
798
  end
706
799
 
707
800
  return is_final
708
801
  end
802
+
803
+ private :_do_values_have_a_three_in_a_row, :_generic_check_try_placing_last_digit, :_are_values_duplicates
709
804
  end
710
805
 
711
806
  class RowHandle
@@ -719,8 +814,14 @@ module Binary_Puzzle_Solver
719
814
  return view.get_row_summary(:idx => idx, :dim => row_dim());
720
815
  end
721
816
 
817
+ def self.calc_iter_of_states_str(iter)
818
+ return iter.map { |v| Cell.get_state_char(v) }.join('')
819
+ end
820
+
722
821
  def get_string()
723
- return iter_of_handles().map { |cell_h| cell_h.get_char() }.join('')
822
+ return RowHandle.calc_iter_of_states_str(
823
+ iter_of_handles().map { |cell_h| cell_h.get_state() }
824
+ )
724
825
  end
725
826
 
726
827
  def col_dim()
@@ -770,21 +871,12 @@ module Binary_Puzzle_Solver
770
871
  x.get_state() == v }.map { |h| h.x }
771
872
  end
772
873
 
773
- def check_for_duplicated(complete_rows_map)
874
+ def check_for_duplicated()
774
875
  summary = get_summary()
775
876
 
776
877
  if not summary.are_both_not_exceeded() then
777
878
  raise GameIntegrityException, "Value exceeded"
778
879
  elsif summary.are_both_full() then
779
- s = get_string()
780
- complete_rows_map[s] ||= []
781
- dups = complete_rows_map[s]
782
- dups << idx
783
- if (dups.length > 1)
784
- i, j = dups[0], dups[1]
785
- raise GameIntegrityException, \
786
- "Duplicate Rows - #{i} and #{j}"
787
- end
788
880
  return true
789
881
  else
790
882
  return false
@@ -823,11 +915,9 @@ module Binary_Puzzle_Solver
823
915
  end
824
916
 
825
917
  def validate(params)
826
- complete_rows_map = params[:complete_rows_map]
827
-
828
918
  check_for_too_many_consecutive()
829
919
 
830
- return { :is_final => check_for_duplicated(complete_rows_map), };
920
+ return { :is_final => check_for_duplicated(), };
831
921
  end
832
922
  end
833
923
 
@@ -1,3 +1,3 @@
1
1
  module Binary_Puzzle_Solver
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
@@ -0,0 +1,12 @@
1
+ | 1 1 0|
2
+ | 0 1 1 |
3
+ | 1 0 0 0|
4
+ |00 1 |
5
+ | 00 0 |
6
+ | 0 0 1 |
7
+ | 00 0 0|
8
+ | 00 1 |
9
+ | 0 1 |
10
+ |00 0 |
11
+ | 00 |
12
+ |00 0 |
@@ -0,0 +1,12 @@
1
+ |0 0 1 1 |
2
+ | 1 0 |
3
+ | 11 0 |
4
+ |0 1 01 0|
5
+ | 0 0|
6
+ | 1 |
7
+ | 0 11 1|
8
+ |1 1 0 |
9
+ | 00 0 0 0|
10
+ | |
11
+ | 1 1 0 0 |
12
+ | 0 10|
data/test/deduction.rb CHANGED
@@ -444,6 +444,102 @@ EOF
444
444
  return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
445
445
  end
446
446
 
447
+ def get_6x6_very_hard_board_1__initial()
448
+ input_str = <<'EOF'
449
+ | 1 |
450
+ |00 1 |
451
+ |0 |
452
+ | |
453
+ | 1 |
454
+ | 0 |
455
+ EOF
456
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
457
+ end
458
+
459
+ def get_6x6_very_hard_board_1__intermediate_1()
460
+ input_str = <<'EOF'
461
+ |101010|
462
+ |001101|
463
+ |010011|
464
+ |1 010|
465
+ | 10 |
466
+ | 1010 |
467
+ EOF
468
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
469
+ end
470
+
471
+ def get_6x6_very_hard_board_1__final()
472
+ input_str = <<'EOF'
473
+ |101010|
474
+ |001101|
475
+ |010011|
476
+ |110010|
477
+ |101100|
478
+ |010101|
479
+ EOF
480
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
481
+ end
482
+
483
+ def get_6x6_very_hard_board_2__initial()
484
+ input_str = <<'EOF'
485
+ | 0 |
486
+ | |
487
+ |1 |
488
+ | 0 0 |
489
+ |1 0 |
490
+ | 0 0|
491
+ EOF
492
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
493
+ end
494
+
495
+ def get_6x6_very_hard_board_2__final()
496
+ input_str = <<'EOF'
497
+ |001011|
498
+ |010011|
499
+ |110100|
500
+ |001101|
501
+ |110010|
502
+ |101100|
503
+ EOF
504
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
505
+ end
506
+
507
+ def get_12x12_very_hard_board_1__initial()
508
+ input_str = <<'EOF'
509
+ | 00 1 |
510
+ | 1 1 |
511
+ | 0 0 0 0|
512
+ | 0 |
513
+ | 11 1 1 0 |
514
+ | 11 0 00 |
515
+ | 1 |
516
+ |1 00 0 |
517
+ | |
518
+ | 1 1 |
519
+ | 0 0 0 0|
520
+ | 10 0 |
521
+ EOF
522
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
523
+ end
524
+
525
+ def get_12x12_very_hard_board_1__intermediate_1()
526
+ input_str = <<'EOF'
527
+ |110100100110|
528
+ |001101001011|
529
+ |110010110100|
530
+ |100100101101|
531
+ |011011010010|
532
+ |011001001101|
533
+ |100110110010|
534
+ |101001010011|
535
+ |010110101100|
536
+ |001101011001|
537
+ |110011010010|
538
+ |001010101101|
539
+ EOF
540
+ return Binary_Puzzle_Solver.gen_board_from_string_v1(input_str)
541
+ end
542
+
447
543
  describe "construct_board" do
448
544
  it "6*6 Easy board No. 1 should" do
449
545
 
@@ -995,4 +1091,97 @@ describe "rudimentary_deduction" do
995
1091
 
996
1092
  compare_boards(board, final_board)
997
1093
  end
1094
+
1095
+ it "Solving 6*6 Very Hard board No. 1 should" do
1096
+
1097
+ board = get_6x6_very_hard_board_1__initial()
1098
+
1099
+ board.add_to_iters_quota(1_000_000_000);
1100
+
1101
+ board.try_to_solve_using(
1102
+ :methods => [
1103
+ :check_and_handle_sequences_in_row,
1104
+ :check_and_handle_known_unknown_sameknown_in_row,
1105
+ :check_and_handle_cells_of_one_value_in_row_were_all_found,
1106
+ :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
1107
+ :check_try_placing_last_of_certain_digit_in_row,
1108
+ ]
1109
+ );
1110
+
1111
+ intermediate_board = get_6x6_very_hard_board_1__intermediate_1()
1112
+
1113
+ # binding.pry
1114
+
1115
+ compare_boards(board, intermediate_board)
1116
+
1117
+ board.try_to_solve_using(
1118
+ :methods => [
1119
+ :check_and_handle_sequences_in_row,
1120
+ :check_and_handle_known_unknown_sameknown_in_row,
1121
+ :check_and_handle_cells_of_one_value_in_row_were_all_found,
1122
+ :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
1123
+ :check_try_placing_last_of_certain_digit_in_row,
1124
+ :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
1125
+ ]
1126
+ );
1127
+
1128
+ board.get_cell_state(
1129
+ Binary_Puzzle_Solver::Coord.new(:x => 1, :y => 3)
1130
+ ).should == ONE
1131
+ board.get_cell_state(
1132
+ Binary_Puzzle_Solver::Coord.new(:x => 2, :y => 3)
1133
+ ).should == ZERO
1134
+
1135
+ final_board = get_6x6_very_hard_board_1__final()
1136
+
1137
+ compare_boards(board, final_board)
1138
+ end
1139
+
1140
+ it "Solving 6*6 Very Hard board No. 2 should" do
1141
+
1142
+ board = get_6x6_very_hard_board_2__initial()
1143
+
1144
+ board.add_to_iters_quota(1_000_000_000);
1145
+
1146
+ board.try_to_solve_using(
1147
+ :methods => [
1148
+ :check_and_handle_sequences_in_row,
1149
+ :check_and_handle_known_unknown_sameknown_in_row,
1150
+ :check_and_handle_cells_of_one_value_in_row_were_all_found,
1151
+ :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
1152
+ :check_try_placing_last_of_certain_digit_in_row,
1153
+ :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
1154
+ ]
1155
+ );
1156
+
1157
+ final_board = get_6x6_very_hard_board_2__final()
1158
+
1159
+ # binding.pry
1160
+
1161
+ compare_boards(board, final_board)
1162
+ end
1163
+
1164
+ it "Solving 12*12 Very Hard board No. 1 should" do
1165
+
1166
+ board = get_12x12_very_hard_board_1__initial()
1167
+
1168
+ board.add_to_iters_quota(1_000_000_000);
1169
+
1170
+ board.try_to_solve_using(
1171
+ :methods => [
1172
+ :check_and_handle_sequences_in_row,
1173
+ :check_and_handle_known_unknown_sameknown_in_row,
1174
+ :check_and_handle_cells_of_one_value_in_row_were_all_found,
1175
+ :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
1176
+ :check_try_placing_last_of_certain_digit_in_row,
1177
+ :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
1178
+ ]
1179
+ );
1180
+
1181
+ final_board = get_12x12_very_hard_board_1__intermediate_1()
1182
+
1183
+ # binding.pry
1184
+
1185
+ compare_boards(board, final_board)
1186
+ end
998
1187
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: binary_puzzle_solver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-28 00:00:00.000000000 Z
12
+ date: 2013-06-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: launchy
@@ -64,6 +64,8 @@ files:
64
64
  - lib/binary_puzzle_solver.rb
65
65
  - lib/binary_puzzle_solver/base.rb
66
66
  - lib/binary_puzzle_solver/version.rb
67
+ - test/data/boards/12x12_hard_no_1.binpuz.board
68
+ - test/data/boards/2013-06-07-12x12_very_hard.binpuz.board
67
69
  - test/deduction.rb
68
70
  homepage: http://www.shlomifish.org/open-source/projects/japanese-puzzle-games/binary-puzzle/
69
71
  licenses: []
@@ -90,4 +92,6 @@ signing_key:
90
92
  specification_version: 3
91
93
  summary: A solver for http://www.binarypuzzle.com/ instances
92
94
  test_files:
95
+ - test/data/boards/12x12_hard_no_1.binpuz.board
96
+ - test/data/boards/2013-06-07-12x12_very_hard.binpuz.board
93
97
  - test/deduction.rb