binary_puzzle_solver 0.0.4 → 0.0.5

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