binary_puzzle_solver 0.0.3 → 0.0.4

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