From: Mike Harris Date: 2006-01-27T10:16:53+09:00 Subject: Re: Grid Folding (#63) I was waiting to clean up my solution, but it's Thursday, so here it is. It's too late for the summary, I'm just looking to get my first successful RubyQuiz up. I also used this as my first TDD "project," which explains why it's so verbose in places. I've also included the tests, can't hurt. class String def each_char for i in 0...length yield(self[i,1]) end end end class Array alias_method :old_eq, :== def ==(arg) return arg == self if arg.kind_of?(Grid) old_eq(arg) end end class Range def middle (first+last)/2 end def flip(i) last-1-(i-first) end def flip_doubled(i) (last*2)-1-(i-first) end end module DiscreteMethod def make_disc_meth(sym) define_method(sym) do |*args| temp_var = clone temp_var.send(sym.to_s + "!",*args) temp_var end end end class Grid extend DiscreteMethod attr_accessor :size, :rows, :cols, :vert, :cells def initialize(n = 0) @size = n @rows = 0...n @cols = 0...n @vert = 0...1 @cells = {} init_cells if n > 0 end def init_cells num = 1 for r in 0...size for c in 0...size @cells[[r,c,0]] = num; num += 1; end end end def [](r,c,v) @cells[[r,c,v]] end make_disc_meth "fold" def fold!(str) if str.length > 1 str.each_char { |c| fold!(c) } return end raise "bad attempted fold" unless ["R","T","L","B"].include?(str) raise "bad attempted fold" if (str == "R" or str == "L") and cols.first == cols.last-1 raise "bad attempted fold" if (str == "B" or str == "T") and rows.first == rows.last-1 @cells.keys.find_all { |k| k[1] >= cols.middle }.each { |k| self[k[0],cols.flip(k[1]),vert.flip_doubled(k[2])] = @cells[k]; @cells.delete(k) } if str == "R" @cells.keys.find_all { |k| k[1] < cols.middle }.each { |k| self[k[0],cols.flip(k[1]),vert.flip_doubled(k[2])] = @cells[k]; @cells.delete(k) } if str == "L" @cells.keys.find_all { |k| k[0] >= rows.middle }.each { |k| self[rows.flip(k[0]),k[1],vert.flip_doubled(k[2])] = @cells[k]; @cells.delete(k) } if str == "B" @cells.keys.find_all { |k| k[0] < rows.middle }.each { |k| self[rows.flip(k[0]),k[1],vert.flip_doubled(k[2])] = @cells[k]; @cells.delete(k) } if str == "T" update_boundaries!(str) end make_disc_meth "update_boundaries" def update_boundaries!(d) @vert = (0...vert.last*2) @rows = (rows.first...rows.middle) if d == "B" @rows = (rows.middle...rows.last) if d == "T" @cols = (cols.first...cols.middle) if d == "R" @cols = (cols.middle...cols.last) if d == "L" end def []=(r,c,v,n) @cells[[r,c,v]] = n end def clone g = Grid.new g.rows,g.cols,g.vert = rows.clone,cols.clone,vert.clone @cells.each_key do |k| g[k[0],k[1],k[2]] = @cells[k] end g end def ==(g) return to_a == g if g.kind_of? Array return false if @cells.keys.size != g.cells.keys.size return false if size != g.size or cols != g.cols or rows != g.rows or vert != g.vert @cells.keys.each { |k| return false if @cells[k] != g.cells[k] } true end def sorted_keys @cells.keys.sort { |a,b| x = b[2] <=> a[2]; (x = a[0] <=> b[0]) if x==0; (x = a[1] <=> b[1]) if x==0; x } end def to_a a = [] sorted_keys.each { |k| a << @cells[k] } a end end def fold(r,c,str) raise "Bad Input Dimensions" if r != c or ![1,2,4,8,16,32,64,128].include?(r) g = Grid.new(r) g.fold!(str) g.to_a end ----------- require 'test/unit' require '../grid.rb' class TestGridFold < Test::Unit::TestCase def setup @two = Grid.new(2) @two_folded_rb = Grid.new @two_folded_rb.rows = (0...1) @two_folded_rb.cols = (0...1) @two_folded_rb.vert = (0...4) @two_folded_rb[0,0,3] = 3 @two_folded_rb[0,0,2] = 4 @two_folded_rb[0,0,1] = 2 @two_folded_rb[0,0,0] = 1 @st = Grid.new(16) end def test_size assert @two.size == 2 end def test_get assert @two[0,0,0] == 1 assert @two[0,1,0] == 2 assert @two[1,0,0] == 3 assert @two[1,1,0] == 4 end def test_out_bounds assert @two[2,2,2].nil? end def test_full_fold g = @two.fold("RB") assert g == @two_folded_rb,g.to_a #assert @two.fold("TL") == [3,1,2,4] end def test_one_fold g = @two.fold("R") assert g == [2,4,1,3],g.to_a g = g.fold("B") assert g == @two_folded_rb end def test_fold_not_modify @two.fold("R") test_get end def test_fold_exc assert @two.fold("RB") == @two_folded_rb end def test_init_boundaries assert @two.rows == (0...2) assert @two.cols == (0...2) assert @two.vert == (0...1) end def test_boundaries_after_fold assert @two.fold("R").cols == (0...1) assert @two.fold("L").cols == (1...2) assert @two.fold("R").rows == (0...2) assert @two.fold("L").rows == (0...2) end def test_update_boundaries_r g = @two.update_boundaries("R") assert g.rows == (0...2) assert g.cols == (0...1),g.cols assert g.vert == (0...2) end def test_update_boundaries_t assert @two.update_boundaries("T").rows == (1...2) assert @two.update_boundaries("T").cols == (0...2) assert @two.update_boundaries("T").vert == (0...2) end def test_update_boundaries_l g = @two.update_boundaries("L") assert g.rows == (0...2) assert g.cols == (1...2),g.cols assert g.vert == (0...2) end def test_update_boundaries_ll g = @st.update_boundaries("L").update_boundaries("L") assert g.rows == (0...16) assert g.cols == (12...16),g.cols assert g.vert == (0...4) end def test_update_boundaries_llrbt g = @st.update_boundaries("L").update_boundaries("L").update_boundaries("R").update_boundaries("B").update_boundaries("T") assert g.rows == (4...8) assert g.cols == (12...14),g.cols assert g.vert == (0...32) end def test_update_boundaries_no_update @two.update_boundaries("R") assert @two.cols == (0...2) end def test_update_boundaries_exc_update @two.update_boundaries!("R") assert @two.cols == (0...1) end def test_clone g = @two.clone assert g[0,0,0] == 1 assert g[1,1,0] == 4 g[0,0,0] = 9 assert g[0,0,0] == 9 assert @two[0,0,0] == 1 end def test_set @two[0,0,0] = 9 assert @two[0,0,0] == 9 end def test_equality_grid assert @two == Grid.new(2) assert Grid.new(2).fold("R") == Grid.new(2).fold("R") assert Grid.new(2).fold("RB") == Grid.new(2).fold("RB") end def test_equality_array assert @two_folded_rb == [3,4,2,1] end def test_to_a a = @two_folded_rb.to_a assert a == [3,4,2,1],a end end