binary_puzzle_solver 0.0.4 → 0.0.5

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.
Files changed (46) hide show
  1. data/bin/binary-puzzle-solve +2 -0
  2. data/lib/binary_puzzle_solver/base.rb +219 -53
  3. data/lib/binary_puzzle_solver/version.rb +1 -1
  4. data/test/data/boards/binarypuzzle.com/numbered/10x10/easy/1/final.binpuz-board.txt +10 -0
  5. data/test/data/boards/binarypuzzle.com/numbered/10x10/easy/1/initial.binpuz-board.txt +10 -0
  6. data/test/data/boards/binarypuzzle.com/numbered/10x10/hard/1/final.binpuz-board.txt +10 -0
  7. data/test/data/boards/binarypuzzle.com/numbered/10x10/hard/1/initial.binpuz-board.txt +10 -0
  8. data/test/data/boards/binarypuzzle.com/numbered/10x10/hard/2/final.binpuz-board.txt +10 -0
  9. data/test/data/boards/binarypuzzle.com/numbered/10x10/hard/2/initial.binpuz-board.txt +10 -0
  10. data/test/data/boards/binarypuzzle.com/numbered/12x12/hard/1/final.binpuz-board.txt +12 -0
  11. data/test/data/boards/binarypuzzle.com/numbered/12x12/hard/1/initial.binpuz-board.txt +12 -0
  12. data/test/data/boards/binarypuzzle.com/numbered/12x12/very_hard/1/initial.binpuz-board.txt +12 -0
  13. data/test/data/boards/binarypuzzle.com/numbered/12x12/very_hard/1/intermediate_1.binpuz-board.txt +12 -0
  14. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/1/final.binpuz-board.txt +14 -0
  15. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/1/initial.binpuz-board.txt +14 -0
  16. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/1/intermediate_1.binpuz-board.txt +14 -0
  17. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/1/intermediate_2.binpuz-board.txt +14 -0
  18. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/2/final.binpuz-board.txt +14 -0
  19. data/test/data/boards/binarypuzzle.com/numbered/14x14/very_hard/2/initial.binpuz-board.txt +14 -0
  20. data/test/data/boards/binarypuzzle.com/numbered/6x6/easy/1/initial.binpuz-board.txt +6 -0
  21. data/test/data/boards/binarypuzzle.com/numbered/6x6/easy/1/intermediate_1.binpuz-board.txt +6 -0
  22. data/test/data/boards/binarypuzzle.com/numbered/6x6/easy/2/initial.binpuz-board.txt +6 -0
  23. data/test/data/boards/binarypuzzle.com/numbered/6x6/easy/2/intermediate_1.binpuz-board.txt +6 -0
  24. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/1/final.binpuz-board.txt +6 -0
  25. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/1/initial.binpuz-board.txt +6 -0
  26. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/1/intermediate.binpuz-board.txt +6 -0
  27. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/2/initial.binpuz-board.txt +6 -0
  28. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/2/intermediate.binpuz-board.txt +6 -0
  29. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/2/intermediate_2.binpuz-board.txt +6 -0
  30. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/3/initial.binpuz-board.txt +6 -0
  31. data/test/data/boards/binarypuzzle.com/numbered/6x6/hard/3/intermediate.binpuz-board.txt +6 -0
  32. data/test/data/boards/binarypuzzle.com/numbered/6x6/medium/1/after_cells_of_one_value_in_row_were_all_found.binpuz-board.txt +6 -0
  33. data/test/data/boards/binarypuzzle.com/numbered/6x6/medium/1/after_easy_moves.binpuz-board.txt +6 -0
  34. data/test/data/boards/binarypuzzle.com/numbered/6x6/medium/1/final.binpuz-board.txt +6 -0
  35. data/test/data/boards/binarypuzzle.com/numbered/6x6/medium/1/initial.binpuz-board.txt +6 -0
  36. data/test/data/boards/binarypuzzle.com/numbered/6x6/very_hard/1/final.binpuz-board.txt +6 -0
  37. data/test/data/boards/binarypuzzle.com/numbered/6x6/very_hard/1/initial.binpuz-board.txt +6 -0
  38. data/test/data/boards/binarypuzzle.com/numbered/6x6/very_hard/1/intermediate_1.binpuz-board.txt +6 -0
  39. data/test/data/boards/binarypuzzle.com/numbered/6x6/very_hard/2/final.binpuz-board.txt +6 -0
  40. data/test/data/boards/binarypuzzle.com/numbered/6x6/very_hard/2/initial.binpuz-board.txt +6 -0
  41. data/test/data/boards/binarypuzzle.com/numbered/8x8/easy/1/final.binpuz-board.txt +8 -0
  42. data/test/data/boards/binarypuzzle.com/numbered/8x8/easy/1/initial.binpuz-board.txt +8 -0
  43. data/test/data/boards/binarypuzzle.com/numbered/8x8/medium/1/final.binpuz-board.txt +8 -0
  44. data/test/data/boards/binarypuzzle.com/numbered/8x8/medium/1/initial.binpuz-board.txt +8 -0
  45. data/test/deduction.rb +190 -388
  46. metadata +84 -2
@@ -18,6 +18,8 @@ board.try_to_solve_using(
18
18
  :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
19
19
  :check_try_placing_last_of_certain_digit_in_row,
20
20
  :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
21
+ :check_remaining_gap_of_three_with_implicits,
22
+ :check_exceeded_digits_taking_large_gaps_into_account,
21
23
  ]
22
24
  );
23
25
 
@@ -166,7 +166,19 @@ module Binary_Puzzle_Solver
166
166
  end
167
167
  end
168
168
 
169
- class Board
169
+ class BaseClass
170
+ def opposite_value(val)
171
+ if (val == Cell::ZERO)
172
+ return Cell::ONE
173
+ elsif (val == Cell::ONE)
174
+ return Cell::ZERO
175
+ else
176
+ raise RuntimeError, "'#{val}' must be zero or one."
177
+ end
178
+ end
179
+ end
180
+
181
+ class Board < BaseClass
170
182
 
171
183
  attr_reader :iters_quota, :num_iters_done
172
184
 
@@ -324,18 +336,34 @@ module Binary_Puzzle_Solver
324
336
  return @row_summaries[dim][idx]
325
337
  end
326
338
 
327
- def opposite_value(val)
328
- if (val == Cell::ZERO)
329
- return Cell::ONE
330
- elsif (val == Cell::ONE)
331
- return Cell::ZERO
332
- else
333
- raise RuntimeError, "'#{val}' must be zero or one."
339
+ def _validate_method_list (methods_list)
340
+ valid_methods_arr = [
341
+ :check_and_handle_sequences_in_row,
342
+ :check_and_handle_known_unknown_sameknown_in_row,
343
+ :check_and_handle_cells_of_one_value_in_row_were_all_found,
344
+ :check_exceeded_numbers_while_accounting_for_two_unknown_gaps,
345
+ :check_try_placing_last_of_certain_digit_in_row,
346
+ :check_try_placing_last_of_certain_digit_in_row_to_avoid_dups,
347
+ :check_remaining_gap_of_three_with_implicits,
348
+ :check_exceeded_digits_taking_large_gaps_into_account,
349
+ ]
350
+
351
+ valid_mathods_hash = valid_methods_arr.inject({}) {
352
+ |h,v| h[v] = true; h
353
+ }
354
+
355
+ methods_list.each do |m|
356
+ if not valid_mathods_hash.has_key?(m) then
357
+ raise RuntimeError, "Method #{m} is not valid to be used."
358
+ end
334
359
  end
335
360
  end
336
361
 
362
+
337
363
  def try_to_solve_using (params)
338
364
  methods_list = params[:methods]
365
+ _validate_method_list(methods_list)
366
+
339
367
  views = list_views()
340
368
 
341
369
  catch :out_of_iters do
@@ -620,53 +648,11 @@ module Binary_Puzzle_Solver
620
648
 
621
649
  row = get_row_handle(row_idx)
622
650
 
623
- gaps = {}
624
-
625
- next_gap = []
626
-
627
- add_gap = lambda {
628
- l = next_gap.length
629
- if (l > 0)
630
- if not gaps[l]
631
- gaps[l] = []
632
- end
633
- gaps[l] << next_gap
634
- next_gap = []
635
- end
636
- }
637
-
638
- row.iter_of_handles().each do |cell_h|
639
- if (cell_h.get_state == Cell::UNKNOWN)
640
- next_gap << cell_h.x
641
- else
642
- add_gap.call()
643
- end
644
- end
645
-
646
- add_gap.call()
651
+ gaps = row.calc_gaps()
647
652
 
648
653
  if (gaps.has_key?(2)) then
649
- implicit_counts = {Cell::ZERO => 0, Cell::ONE => 0,}
650
- gaps[2].each do |gap|
651
- x_s = []
652
- if (gap[0] > 0)
653
- x_s << gap[0]-1
654
- end
655
- if (gap[-1] < row.max_idx)
656
- x_s << gap[-1]+1
657
- end
658
654
 
659
- bordering_values = {Cell::ZERO => 0, Cell::ONE => 0,}
660
- x_s.each do |x|
661
- bordering_values[row.get_state(x)] += 1
662
- end
663
-
664
- for v in [Cell::ZERO, Cell::ONE] do
665
- if bordering_values[opposite_value(v)] > 0
666
- implicit_counts[v] += 1
667
- end
668
- end
669
- end
655
+ implicit_counts = row.calc_gaps_implicit_counts(gaps)
670
656
 
671
657
  summ = row.get_summary()
672
658
 
@@ -788,6 +774,131 @@ module Binary_Puzzle_Solver
788
774
  return
789
775
  end
790
776
 
777
+ def check_remaining_gap_of_three_with_implicits(params)
778
+ row_idx = params[:idx]
779
+
780
+ row = get_row_handle(row_idx)
781
+
782
+ gaps = row.calc_gaps()
783
+
784
+ if (gaps.has_key?(2) and gaps.has_key?(3)) then
785
+
786
+ implicit_counts = row.calc_gaps_implicit_counts(gaps)
787
+
788
+ summ = row.get_summary()
789
+
790
+ v = [Cell::ZERO, Cell::ONE].find {
791
+ |v| summ.get_count(v) + implicit_counts[v] \
792
+ == summ.half_limit() - 1
793
+ }
794
+
795
+ if v then
796
+ opposite_val = opposite_value(v)
797
+ gap_3 = gaps[3][0]
798
+
799
+
800
+ # Copied - refactor
801
+ for idx in [0,-1] do
802
+ opposite_idx = (idx == 0) ? -1 : 0
803
+ edge_offset = (idx == 0) ? (-1) : 1
804
+ if (gap_3[idx] > 0 and gap_3[idx] < row.max_idx()) then
805
+ if (row.get_state(gap_3[idx]+edge_offset) \
806
+ == opposite_val) then
807
+ perform_and_append_move(
808
+ :coord => row.get_coord(gap_3[opposite_idx]),
809
+ :val => opposite_val,
810
+ :reason => \
811
+ "Gap of 3 with 2 of the value remaining must not form 3 of a kind (with implicits)",
812
+ :dir => row.col_dim()
813
+ )
814
+ end
815
+ end
816
+ end
817
+ end
818
+ end
819
+
820
+ return
821
+ end
822
+
823
+ def check_exceeded_digits_taking_large_gaps_into_account(params)
824
+ row_idx = params[:idx]
825
+
826
+ row = get_row_handle(row_idx)
827
+
828
+ gaps = row.calc_gaps()
829
+ summ = row.get_summary()
830
+
831
+ values_sorted = [Cell::ZERO, Cell::ONE].sort { |a,b|
832
+ summ.get_count(a) <=> summ.get_count(b) }
833
+
834
+ v = values_sorted[-1]
835
+
836
+ to_add = 0
837
+ (3 .. row.max_idx()).each do |len|
838
+ if gaps.has_key?(len) then
839
+ to_add += gaps[len].length
840
+ end
841
+ end
842
+
843
+ if summ.get_count(v) + to_add == summ.half_limit() then
844
+ opposite_val = opposite_value(v)
845
+ (3 .. row.max_idx()).each do |len|
846
+ if gaps.has_key?(len) then
847
+ if (len == 3) then
848
+ gaps[len].each do |gap_3|
849
+ # Copied - refactor
850
+ for idx in [0,-1] do
851
+ opposite_idx = (idx == 0) ? -1 : 0
852
+ edge_offset = (idx == 0) ? (-1) : 1
853
+ if (gap_3[idx] > 0 and gap_3[idx] < row.max_idx()) then
854
+ if (row.get_state(gap_3[idx]+edge_offset) \
855
+ == opposite_val) then
856
+ perform_and_append_move(
857
+ :coord => row.get_coord(gap_3[opposite_idx]),
858
+ :val => opposite_val,
859
+ :reason => \
860
+ "With gaps of 3, exceeded value cannot be at the opposite edge of the opposite value.",
861
+ :dir => row.col_dim()
862
+ )
863
+ end
864
+ end
865
+ end
866
+ end
867
+ elsif (len == 4) then
868
+ gaps[len].each do |gap|
869
+ [0, -1].each do |i|
870
+ perform_and_append_move(
871
+ :coord => row.get_coord(gap[i]),
872
+ :val => opposite_val,
873
+ :reason => \
874
+ "With gaps of 4, exceeded value cannot be at edges.",
875
+ :dir => row.col_dim()
876
+ )
877
+ end
878
+ end
879
+ elsif (len == 5) then
880
+ gaps[len].each do |gap|
881
+ o = opposite_val
882
+ gap.zip([o,o,v,o,o]) do |pos_v|
883
+ perform_and_append_move(
884
+ :coord => row.get_coord(gap[pos_v[0]]),
885
+ :val => pos_v[1],
886
+ :reason => \
887
+ "With gaps of 5, exceeded value pattern must be 00100.",
888
+ :dir => row.col_dim()
889
+ )
890
+ end
891
+ end
892
+ else # len >= 5
893
+ raise GameIntegrityException, "Too large a gap."
894
+ end
895
+ end
896
+ end
897
+ end
898
+
899
+ return
900
+ end
901
+
791
902
  def validate_rows()
792
903
  is_final = true
793
904
 
@@ -803,7 +914,7 @@ module Binary_Puzzle_Solver
803
914
  private :_do_values_have_a_three_in_a_row, :_generic_check_try_placing_last_digit, :_are_values_duplicates
804
915
  end
805
916
 
806
- class RowHandle
917
+ class RowHandle < BaseClass
807
918
  attr_reader :view, :idx
808
919
  def initialize (init_view, init_idx)
809
920
  @view = init_view
@@ -919,6 +1030,61 @@ module Binary_Puzzle_Solver
919
1030
 
920
1031
  return { :is_final => check_for_duplicated(), };
921
1032
  end
1033
+
1034
+ def calc_gaps
1035
+ gaps = {}
1036
+
1037
+ next_gap = []
1038
+
1039
+ add_gap = lambda {
1040
+ l = next_gap.length
1041
+ if (l > 0)
1042
+ if not gaps[l]
1043
+ gaps[l] = []
1044
+ end
1045
+ gaps[l] << next_gap
1046
+ next_gap = []
1047
+ end
1048
+ }
1049
+
1050
+ iter_of_handles().each do |cell_h|
1051
+ if (cell_h.get_state == Cell::UNKNOWN)
1052
+ next_gap << cell_h.x
1053
+ else
1054
+ add_gap.call()
1055
+ end
1056
+ end
1057
+
1058
+ add_gap.call()
1059
+
1060
+ return gaps
1061
+ end
1062
+
1063
+ def calc_gaps_implicit_counts(gaps)
1064
+ implicit_counts = {Cell::ZERO => 0, Cell::ONE => 0,}
1065
+ gaps[2].each do |gap|
1066
+ x_s = []
1067
+ if (gap[0] > 0)
1068
+ x_s << gap[0]-1
1069
+ end
1070
+ if (gap[-1] < max_idx())
1071
+ x_s << gap[-1]+1
1072
+ end
1073
+
1074
+ bordering_values = {Cell::ZERO => 0, Cell::ONE => 0,}
1075
+ x_s.each do |x|
1076
+ bordering_values[get_state(x)] += 1
1077
+ end
1078
+
1079
+ for v in [Cell::ZERO, Cell::ONE] do
1080
+ if bordering_values[opposite_value(v)] > 0
1081
+ implicit_counts[v] += 1
1082
+ end
1083
+ end
1084
+ end
1085
+
1086
+ return implicit_counts
1087
+ end
922
1088
  end
923
1089
 
924
1090
  class CellHandle
@@ -1,3 +1,3 @@
1
1
  module Binary_Puzzle_Solver
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -0,0 +1,10 @@
1
+ |0110010101|
2
+ |1001100110|
3
+ |1001101010|
4
+ |0110011001|
5
+ |1010100101|
6
+ |0101010110|
7
+ |1001101001|
8
+ |0110110100|
9
+ |1010011010|
10
+ |0101001011|
@@ -0,0 +1,10 @@
1
+ | 1 |
2
+ | 00 0 1 |
3
+ | 0 1 0 0|
4
+ | 1 1 |
5
+ |1 1 1|
6
+ | 1 |
7
+ | 0 1 0 |
8
+ | 11 0|
9
+ | 0 0 1 0|
10
+ |0 0 1 |
@@ -0,0 +1,10 @@
1
+ |1101100100|
2
+ |0101011001|
3
+ |0010101011|
4
+ |1011010100|
5
+ |1100110010|
6
+ |0011001101|
7
+ |0100110110|
8
+ |1001001011|
9
+ |0110010101|
10
+ |1010101010|
@@ -0,0 +1,10 @@
1
+ | 0 |
2
+ |0 0 1 |
3
+ | 1 |
4
+ | 0 0|
5
+ |1 00 1 |
6
+ | |
7
+ | 0 1 1 |
8
+ |1 11|
9
+ | 0 |
10
+ |1 1 1 1 |
@@ -0,0 +1,10 @@
1
+ |0110011010|
2
+ |0110100101|
3
+ |1001010101|
4
+ |1001101010|
5
+ |0110010101|
6
+ |1011001010|
7
+ |0101100110|
8
+ |0010110101|
9
+ |1001011010|
10
+ |1100101001|
@@ -0,0 +1,10 @@
1
+ | 0 1 0|
2
+ | 1 1 0 |
3
+ | 0 10 |
4
+ | 0 |
5
+ | 0 |
6
+ | 1 0|
7
+ | 1 0 |
8
+ | 0 0 |
9
+ | 00 0 0|
10
+ | 0 1 0 |
@@ -0,0 +1,12 @@
1
+ |011010101100|
2
+ |010100110011|
3
+ |101101010010|
4
+ |001011001101|
5
+ |110100110010|
6
+ |100100101101|
7
+ |011011001010|
8
+ |100101010101|
9
+ |110010110100|
10
+ |001011001011|
11
+ |110101100100|
12
+ |001010011011|
@@ -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
+ | 00 1 |
2
+ | 1 1 |
3
+ | 0 0 0 0|
4
+ | 0 |
5
+ | 11 1 1 0 |
6
+ | 11 0 00 |
7
+ | 1 |
8
+ |1 00 0 |
9
+ | |
10
+ | 1 1 |
11
+ | 0 0 0 0|
12
+ | 10 0 |
@@ -0,0 +1,12 @@
1
+ |110100100110|
2
+ |001101001011|
3
+ |110010110100|
4
+ |100100101101|
5
+ |011011010010|
6
+ |011001001101|
7
+ |100110110010|
8
+ |101001010011|
9
+ |010110101100|
10
+ |001101011001|
11
+ |110011010010|
12
+ |001010101101|