Conway's Game of Life having logic issues - ruby
I'm working on Conway's Game of Life. I have some logic issue that it always finishes in 2 or 3 ticks (usually only 2). Most of the time, it is with all cells dead, but occasionally it will have a 1 or 2 still alive. I can't find what part is causing the behavior of the second tick being almost entirely, if not completely, dead.
Do any of you see any major issues that might be causing this behavior?
require 'pry'
class Game
def initialize(size)
#size = size
#ticks = 1
#current_board = Array.new(size) { Array.new(size) { (rand(99) % 5 == 0) ? false : true } }
#future_board = Array.new(size) { Array.new(size) }
# #future_board = #current_board
end
def is_alive?(y, x)
if #current_board[y]
#current_board[y][x]
end
end
def check_neigbors(y, x)
neighbors = {
top: [y-1, x],
top_right: [y-1, x+1],
right: [y, x+1],
bottom_right: [y+1, x+1],
bottom: [y+1, x],
bottom_left: [y+1, x-1],
left: [y, x-1],
top_left: [y-1, x-1]
}
neighbor_state = {
top: false,
top_right: false,
right: false,
bottom_right: false,
bottom: false,
bottom_left: false,
left: false,
top_left: false
}
# binding.pry
neighbors.each { |k, v| neighbor_state[k] = true if is_alive?(v[0], v[1]) }
live_neighbor_count = 0
neighbor_state.each_value { |v| live_neighbor_count += 1 if v }
live_neighbor_count
end
def cell_lives(y, x)
#future_board[y][x] = true
end
def cell_dies(y, x)
#future_board[y][x] = false
end
def display_board
# need to display board here
system("clear")
# #current_board.each do
# |r| puts r.map { |c| c ? 'O' : 'X' }.join(" ")
# |r| puts r.map { |c| c }.join(" ")
puts #current_board.map { |row| row.map { |cell| cell ? 'X' : ' ' }.inspect }
# end
puts "\nTicks: #{#ticks}"
end
def play
loop do
display_board
#current_board.each do |r| # outer loop to iterate through rows
row_index = #current_board.index(r).to_i
r.each do |c| # inner loop to iterate through columns
column_index = r.index(c).to_i
live_neighbor_count = check_neigbors(row_index, column_index) # count the number of live neighbors
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count < 2 # rule 1
cell_lives(row_index, column_index) if ( is_alive?(row_index, column_index) ) && ( live_neighbor_count == 2 || live_neighbor_count == 3 ) # rule 2
cell_dies(row_index, column_index) if ( is_alive?(row_index, column_index) ) && live_neighbor_count > 3 # rule 3
cell_lives(row_index, column_index) if !( is_alive?(row_index, column_index) ) && live_neighbor_count == 3 # rule 4
end
end
if #current_board == #future_board # board is gridlocked. Game over!
puts "\nGAME OVER!"
exit!
else
#current_board = #future_board # update the board
#ticks += 1
sleep(1)
end
end
end
end
print "How large of a grid do you want: "
grid_size = gets.to_i
game = Game.new grid_size
game.play
In Conway's game of life, if you start with a random arrangement of cells, most of them do die on the second tick just because of the rules of the game, so I have no evidence there is a bug.
How about you make a glider or something and make sure it behaves as expected? If it doesn't work, then please post the output of your program and point out the first frame where the output is wrong, and post what you would expect the output to be.
try :-
#current_board = #futureboard.dup
Related
How do I fix a problem to call a function in Ruby?
I'm trying to use some ruby code that I've found in Github. I've downloaded the code and did the necessary imports the "requires" and tried to run it as it is described in the readme file on github repository. The code is the following: In the file pcset_test.rb the code is the following: require './pcset.rb' require 'test/unit' # # When possible, test cases are adapted from # Introduction to Post-Tonal Theory by Joseph N. Straus, # unless obvious or otherwise noted. # class PCSetTest < Test::Unit::TestCase def test_init #assert_raise(ArgumentError) {PCSet.new []} assert_raise(ArgumentError) {PCSet.new [1, 2, 3, 'string']} assert_raise(ArgumentError) {PCSet.new "string"} assert_raise(ArgumentError) {PCSet.new [1, 2, 3.6, 4]} assert_equal([0, 1, 2, 9], PCSet.new([0, 1, 2, 33, 13]).pitches) assert_equal([3, 2, 1, 11, 10, 0], PCSet.new_from_string('321bac').pitches) assert_equal([0,2,4,5,7,11,9], PCSet.new([12,2,4,5,7,11,9]).pitches) assert_nothing_raised() {PCSet.new []} end def test_inversion end def test_transposition end def test_multiplication end # # set normal prime forte # # 0,2,4,7,8,11 7,8,11,0,2,4 0,1,4,5,7,9 6-31 # 0,1,2,4,5,7,11 11,0,1,2,4,5,7 0,1,2,3,5,6,8 7-Z36 # 0,1,3,5,6,7,9,10,11 5,6,7,9,10,11,0,1,3 0,1,2,3,4,6,7,8,10 9-8 # def test_normal_form testPC = PCSet.new [0,4,8,9,11] assert_kind_of(PCSet, testPC.normal_form) assert_equal([8,9,11,0,4], testPC.normal_form.pitches) assert_equal([10,1,4,6], PCSet.new([1,6,4,10]).normal_form.pitches) assert_equal([2,4,8,10], PCSet.new([10,8,4,2]).normal_form.pitches) assert_equal([7,8,11,0,2,4], PCSet.new([0,2,4,7,8,11]).normal_form.pitches) assert_equal([11,0,1,2,4,5,7], PCSet.new([0,1,2,4,5,7,11]).normal_form.pitches) assert_equal([5,6,7,9,10,11,0,1,3], PCSet.new([0,1,3,5,6,7,9,10,11]).normal_form.pitches) end def test_prime_form assert_equal([0,1,2,6], PCSet.new([5,6,1,7]).prime.pitches) assert_equal([0,1,4], PCSet.new([2,5,6]).prime.pitches) assert_equal([0,1,4,5,7,9], PCSet.new([0,2,4,7,8,11]).prime.pitches) assert_equal([0,1,2,3,5,6,8], PCSet.new([0,1,2,4,5,7,11]).prime.pitches) assert_equal([0,1,2,3,4,6,7,8,10], PCSet.new([0,1,3,5,6,7,9,10,11]).prime.pitches) end def test_set_class testPcs = PCSet.new([2,5,6]) testPrime = testPcs.prime assert_equal([ [2,5,6], [3,6,7], [4,7,8], [5,8,9], [6,9,10], [7,10,11], [8,11,0],[9,0,1], [10,1,2],[11,2,3],[0,3,4], [1,4,5], [6,7,10],[7,8,11],[8,9,0], [9,10,1],[10,11,2],[11,0,3], [0,1,4], [1,2,5], [2,3,6], [3,4,7], [4,5,8], [5,6,9] ].sort, PCSet.new([2,5,6]).set_class.map{|x| x.pitches}) assert_equal(testPcs.set_class.map{|x| x.pitches}, testPrime.set_class.map{|x| x.pitches}) end def test_interval_vector assert_equal([2,1,2,1,0,0], PCSet.new([0,1,3,4]).interval_vector) assert_equal([2,5,4,3,6,1], PCSet.new([0,1,3,5,6,8,10]).interval_vector) assert_equal([0,6,0,6,0,3], PCSet.new([0,2,4,6,8,10]).interval_vector) end def test_complement assert_equal([6,7,8,9,10,11], PCSet.new([0,1,2,3,4,5]).complement.pitches) assert_equal([3,4,5], PCSet.new([0,1,2], 6).complement.pitches) end # # Test values from (Morris 1991), pages 105-111 # Citation: # Morris. Class Notes for Atonal Music Theory # Lebanon, NH. Frog Peak Music, 1991. # def test_invariance_vector assert_equal([1,0,0,0,5,6,5,5],PCSet.new([0,2,5]).invariance_vector) assert_equal([2,2,2,2,6,6,6,6],PCSet.new([0,1,6,7]).invariance_vector) assert_equal([6,6,6,6,6,6,6,6],PCSet.new([0,2,4,6,8,10]).invariance_vector) assert_equal([1,0,0,0,0,0,0,0],PCSet.new([0,1,2,3,4,5,8]).invariance_vector) assert_equal([1,0,0,1,0,0,0,0],PCSet.new([0,1,2,3,5,6,8]).invariance_vector) assert_equal([12,12,12,12,0,0,0,0],PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).invariance_vector) end # # Test values from (Huron 1994). Huron rounds, thus the 0.01 margin of error. # Citation: # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 # def test_huron h1 = PCSet.new([0,1,2,3,4,5,6,7,8,9,10,11]).huron assert_in_delta(-0.2, h1[0], 0.01) assert_in_delta(0.21, h1[1], 0.01) h2 = PCSet.new([0,2,4,5,7,9,11]).huron assert_in_delta(4.76, h2[0], 0.01) assert_in_delta(0.62, h2[1], 0.01) end def test_coherence end end And in the file pcset.rb the folloing code: # # => PCSet Class for Ruby # => Beau Sievers # => Hanover, Fall 2008. # # # TODO: Make this a module to avoid namespace collisions. # Lilypond and MusicXML output # include Math def choose(n, k) return [[]] if n.nil? || n.empty? && k == 0 return [] if n.nil? || n.empty? && k > 0 return [[]] if n.size > 0 && k == 0 c2 = n.clone c2.pop new_element = n.clone.pop choose(c2, k) + append_all(choose(c2, k-1), new_element) end def append_all(lists, element) lists.map { |l| l << element } end def array_to_binary(array) array.inject(0) {|sum, n| sum + 2**n} end # the following method is horrifically inelegant # but avoids monkey-patching. # TODO: do this right, incl. error checking def pearsons(x, y) if !x.is_a?(Array) || !y.is_a?(Array) then raise StandardError, "x and y must be arrays", caller end if x.size != y.size then raise StandardError, "x and y must be same size", caller end sum_x = x.inject(0) {|sum, n| sum + n} sum_y = y.inject(0) {|sum, n| sum + n} sum_square_x = x.inject(0) {|sum, n| sum + n * n} sum_square_y = y.inject(0) {|sum, n| sum + n * n} xy = [] x.zip(y) {|a, b| xy.push(a * b)} sum_xy = xy.inject(0) {|sum, n| sum + n} num = sum_xy - ((sum_x * sum_y)/x.size) den = Math.sqrt((sum_square_x - ((sum_x*sum_x)/x.size)) * (sum_square_y - ((sum_y*sum_y)/x.size))) (num/den) end class PCSet include Comparable attr_reader :pitches, :base, :input def initialize(pcarray, base = 12) if pcarray.instance_of?(Array) && pcarray.all?{|pc| pc.instance_of?(Fixnum)} #base, #input = base, pcarray #pitches = pcarray.map{ |x| x % #base }.uniq else raise ArgumentError, "Improperly formatted PC array", caller end end def PCSet.new_from_string(pcstring, base = 12) if base > 36 then raise StandardError, "Use PCSet.new to create pcsets with a base larger than 36", caller end pcarray = [] pcstring.downcase.split(//).each do |c| if c <= 'z' and c >= '0' then pcarray.push(c.to_i(36)) end end PCSet.new pcarray, base end def <=>(pcs) #pitches <=> pcs.pitches end def [](index) #pitches[index] end # Intersection def &(other) PCSet.new #pitches & other.pitches end # Union def |(other) PCSet.new #pitches | other.pitches end def inspect #pitches.inspect end def length #pitches.length end def invert(axis = 0) PCSet.new #pitches.map {|x| (axis-x) % #base} end def invert!(axis = 0) #pitches.map! {|x| (axis-x) % #base} end def transpose(interval) PCSet.new #pitches.map {|x| (x + interval) % #base} end def transpose!(interval) #pitches.map! {|x| (x + interval) % #base} end def multiply(m = 5) PCSet.new #pitches.map {|x| (x * m) % #base} end def multiply!(m = 5) #pitches.map! {|x| (x * m) % #base} end def zero transpose(-1 * #pitches[0]) end def zero! transpose!(-1 * #pitches[0]) end def transpositions (0..(#base-1)).to_a.map{|x| #pitches.map {|y| (y + x) % #base}}.sort.map {|x| PCSet.new x} end def transpositions_and_inversions(axis = 0) transpositions + invert(axis).transpositions end # # Normal form after Straus. Morris and AthenaCL do this differently. # def normal_form tempar = #pitches.sort arar = [] # [[1,4,7,8,10],[4,7,8,10,1], etc.] get each cyclic variation tempar.each {arar.push PCSet.new(tempar.unshift(tempar.pop))} most_left_compact(arar) end def normal_form! #pitches = normal_form.pitches end def is_normal_form? self.pitches == self.normal_form.pitches end def set_class transpositions_and_inversions.map{|pcs| pcs.normal_form}.sort end def prime most_left_compact([normal_form.zero, invert.normal_form.zero]) end def prime! self.pitches = self.prime.pitches end def is_prime? self.pitches == self.prime.pitches end def complement new_pitches = [] #base.times do |p| if !#pitches.include? p then new_pitches.push p end end PCSet.new new_pitches end def full_interval_vector pairs = choose(#pitches, 2) # choose every pc pair intervals = pairs.map {|x| (x[1] - x[0]) % #base} # calculate every interval i_vector = Array.new(#base-1).fill(0) intervals.each {|x| i_vector[x-1] += 1} # count the intervals i_vector end def interval_vector i_vector = full_interval_vector (0..((#base-1)/2)-1).each {|x| i_vector[x] += i_vector.pop} i_vector end # # Morris's invariance vector # def invariance_vector(m = 5) t = transpositions.map!{|pcs| self & pcs} ti = invert.transpositions.map!{|pcs| self & pcs} tm = multiply(m).transpositions.map!{|pcs| self & pcs} tmi = invert.multiply(m).transpositions.map!{|pcs| self & pcs} tc = complement.transpositions.map!{|pcs| self & pcs} tic = complement.invert.transpositions.map!{|pcs| self & pcs} tmc = complement.multiply(m).transpositions.map!{|pcs| self & pcs} tmic = complement.invert.multiply(m).transpositions.map!{|pcs| self & pcs} [t, ti, tm, tmi, tc, tic, tmc, tmic].map{|x| x.reject{|pcs| pcs.pitches != #pitches}.length} end # Huron's aggregate dyadic consonance measure. # Huron. Interval-Class Content in Equally Tempered Pitch-Class Sets: # Common Scales Exhibit Optimum Tonal Consonance. # Music Perception (1994) vol. 11 (3) pp. 289-305 def huron if #base != 12 then raise StandardError, "PCSet.huron only makes sense for mod 12 pcsets", caller end # m2/M7 M2/m7 m3/M6 M3/m6 P4/P5 A4/d5 huron_table = [-1.428, -0.582, 0.594, 0.386, 1.240, -0.453] interval_consonance = [] interval_vector.zip(huron_table) {|x, y| interval_consonance.push(x * y) } aggregate_dyadic_consonance = interval_consonance.inject {|sum, n| sum + n} [aggregate_dyadic_consonance, pearsons(interval_vector, huron_table)] end # # Balzano's vector of relations. Citation for all Balzano methods: # # Balzano. "The Pitch Set as a Level of Description for Studying Musical # Pitch Perception" in Music, Mind, and Brain ed. Clynes. Plenum Press. 1982. # def vector_of_relations (0..length-1).to_a.map do |i| (0..length-1).to_a.map do |j| (#pitches[(i + j) % length] - #pitches[i]) % #base end end end # # Checks if the set satisfies Balzano's uniqueness. # def is_unique? vector_of_relations.uniq.size == vector_of_relations.size end # # Checks if the set satisfies Balzano's scalestep-semitone coherence. # For all s[i] and s[i1]: # j < k => v[i][j] < v[i1][k] # Where j and k are scalestep-counting indices. # And unless v[i][j] == 6 (a tritone), in which case the strict inequality is relaxed. # def is_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| if v[i][j] == 6 truth_array.push(v[i][j] <= v[i1][k]) else truth_array.push(v[i][j] < v[i1][k]) end if v[i1][j] == 6 truth_array.push(v[i1][j] <= v[i][k]) else truth_array.push(v[i1][j] < v[i][k]) end end end !truth_array.include?(false) end # # Strict Balzano coherence, no inequality relaxation for tritones. # def is_strictly_coherent? v = vector_of_relations truth_array = [] all_pair_indices = choose((0..length-1).to_a, 2) all_pair_indices.each do |i, i1| all_pair_indices.each do |j, k| truth_array.push(v[i][j] < v[i1][k]) truth_array.push(v[i1][j] < v[i][k]) end end !truth_array.include?(false) end def notes(middle_c = 0) noteArray = ['C','C#','D','D#','E','F','F#','G','G#','A','A#','B'] if #base != 12 then raise StandardError, "PCSet.notes only makes sense for mod 12 pcsets", caller end out_string = String.new transpose(-middle_c).pitches.each do |p| out_string += noteArray[p] + ", " end out_string.chop.chop end def info print "modulo: #{#base}\n" print "raw input: #{#input.inspect}\n" print "pitch set: #{#pitches.inspect}\n" print "notes: #{notes}\n" print "normal: #{normal_form.inspect}\n" print "prime: #{prime.inspect}\n" print "interval vector: #{interval_vector.inspect}\n" print "invariance vector: #{invariance_vector.inspect}\n" print "huron ADC: #{huron[0]} pearsons: #{huron[1]}\n" print "balzano coherence: " if is_strictly_coherent? print "strictly coherent\n" elsif is_coherent? print "coherent\n" else print "false\n" end end # def lilypond # # end # # def musicXML # # end ############################################################################### private # # Convert every pitch array to a binary representation, e.g.: # [0,2,4,8,10] -> 010100010101 # 2^n: BA9876543210 # The smallest binary number is the most left-compact. # def most_left_compact(pcset_array) if !pcset_array.all? {|pcs| pcs.length == pcset_array[0].length} raise ArgumentError, "PCSet.most_left_compact: All PCSets must be of same cardinality", caller end zeroed_pitch_arrays = pcset_array.map {|pcs| pcs.zero.pitches} binaries = zeroed_pitch_arrays.map {|array| array_to_binary(array)} winners = [] binaries.each_with_index do |num, i| if num == binaries.min then winners.push(pcset_array[i]) end end winners.sort[0] end end I'm calling them as follows: > my_pcset = PCSet.new([0,2,4,6,8,10]) > my_pcset2 = PCSet.new([1,5,9]) It shoud return: > my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] > my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] But is returning nothing. The code is available on github Thanks
Try this in terminal: irb -r ./path_to_directory/pcset.rb and then initialize the objects.
I think the documentation for the repo is bad as it does not explain how you should be running this. The result of my_pcset = PCSet.new([0,2,4,6,8,10]) should set my_pcset to an instance of a PCSet not an array, so these lines from the README file are confusing at best. 3. How to use it Make new PCSets: my_pcset = PCSet.new([0,2,4,6,8,10]) => [0, 2, 4, 6, 8, 10] my_pcset2 = PCSet.new([1,5,9]) => [1, 5, 9] Looking at the code, I see inspect has been delegated to #pitches def inspect #pitches.inspect end I think if you inspect my_pcset you will get the expected result. my_pcset = PCSet.new([0,2,4,6,8,10]) p my_pcset # will print [0, 2, 4, 6, 8, 10] or `my_pcset.inspect` will return what you are expecting.
How to optimize code - it works, but I know I'm missing much learning
The exercise I'm working on asks "Write a method, coprime?(num_1, num_2), that accepts two numbers as args. The method should return true if the only common divisor between the two numbers is 1." I've written a method to complete the task, first by finding all the factors then sorting them and looking for duplicates. But I'm looking for suggestions on areas I should consider to optimize it. The code works, but it is just not clean. def factors(num) return (1..num).select { |n| num % n == 0} end def coprime?(num_1, num_2) num_1_factors = factors(num_1) num_2_factors = factors(num_2) all_factors = num_1_factors + num_2_factors new = all_factors.sort dups = 0 new.each_index do |i| dups += 1 if new[i] == new[i+1] end if dups > 1 false else true end end p coprime?(25, 12) # => true p coprime?(7, 11) # => true p coprime?(30, 9) # => false p coprime?(6, 24) # => false
You could use Euclid's algorithm to find the GCD, then check whether it's 1. def gcd a, b while a % b != 0 a, b = b, a % b end return b end def coprime? a, b gcd(a, b) == 1 end p coprime?(25, 12) # => true p coprime?(7, 11) # => true p coprime?(30, 9) # => false p coprime?(6, 24) # => false```
You can just use Integer#gcd: def coprime?(num_1, num_2) num_1.gcd(num_2) == 1 end
You don't need to compare all the factors, just the prime ones. Ruby does come with a Prime class require 'prime' def prime_numbers(num_1, num_2) Prime.each([num_1, num_2].max / 2).map(&:itself) end def factors(num, prime_numbers) prime_numbers.select {|n| num % n == 0} end def coprime?(num_1, num_2) prime_numbers = prime_numbers(num_1, num_2) # & returns the intersection of 2 arrays (https://stackoverflow.com/a/5678143) (factors(num_1, prime_numbers) & factors(num_2, prime_numbers)).length == 0 end
FizzBuzz Program Output in form of table
I have written the logic for the program to perform FizzBuzz operations: fizzbuzz module FizzBuzz class Operation def input puts 'Enter a number upto which Fizz/Buzz needs to be printed' num = gets.chomp.to_i fizzbuzz_function(num) end def fizzbuzz_function(num) for i in 1..num if i % 3 == 0 && i % 5 == 0 puts 'FizzBuzz' elsif i % 3 == 0 puts 'Fizz' elsif i % 5 == 0 puts 'Buzz' else puts i end end end end res = Operation.new res.input end But I am trying to print the output in form of a table.
Here is FizzBuzz in form of a table: def fizzbuzz_gen(num) Enumerator.new do |y| (1..num).each do |i| if i % 3 == 0 && i % 5 == 0 y << 'FizzBuzz' elsif i % 3 == 0 y << 'Fizz' elsif i % 5 == 0 y << 'Buzz' else y << i.to_s end end end end def fill_to_width(width, e) result = "" future_length = -1 while result.length + future_length < width result << e.next result << " " future_length = e.peek.length end result.center(width) end def format_table(num) fb = fizzbuzz_gen(num) begin puts fill_to_width(75, fb) puts fill_to_width(75, fb) loop do puts "%10s%s%31s%s" % ["", fill_to_width(12, fb), "", fill_to_width(12, fb)] end rescue StopIteration end end format_table(100) There may be less numbers output than specified, in order for one leg not to be shorter than another.
ruby code stopping at run time, seemingly an infinite loop
if i run the code, it will stop and not do anything and i am unable to type. seems to be an infinite loop. the problem seems to be the end until loop, however if i take that out, my condition will not be met. can anyone find a solution? i have tried all the loops that i can think of. /. 2d array board ./ board = Array.new(10) { Array.new(10, 0) } /. printing board ./ if board.count(5) != 5 && board.count(4) != 4 && board.count(3) != 3 for i in 0..9 for j in 0..9 board[i][j] = 0 end end aircraftcoord1 = (rand*10).floor aircraftcoord2 = (rand 6).floor aircraftalign = rand if aircraftalign < 0.5 for i in 0..4 board[aircraftcoord2+i][aircraftcoord1] = 5 end else for i in 0..4 board[aircraftcoord1][aircraftcoord2+i] = 5 end end cruisercoord1 = (rand*10).floor cruisercoord2 = (rand 7).floor cruiseralign = rand if cruiseralign < 0.5 for i in 0..3 board[cruisercoord2+i][cruisercoord1] = 4 end else for i in 0..3 board[cruisercoord1][cruisercoord2+i] = 4 end end destroyercoord1 = (rand*10).floor destroyercoord2 = (rand 8).floor destroyeralign = rand if destroyeralign < 0.5 for i in 0..2 board[destroyercoord2+i][destroyercoord1] = 3 end else for i in 0..2 board[destroyercoord1][destroyercoord2+i] = 3 end end end until board.count(5) == 5 && board.count(4) == 4 && board.count(3) == 3 print " " for i in 0..9 print i end puts for i in 0..9 print i for j in 0..9 print board[i][j] end puts end
The line board.count(5) == 5 ... will never be true because board is a two-dimensional array. I can't tell what the condition should be, but it could look something like: board[5].count(5) == 5
Remove duplicate text from multiple strings
I have: a = "This is Product A with property B and propery C. Buy it now!" b = "This is Product B with property X and propery Y. Buy it now!" c = "This is Product C having no properties. Buy it now!" I'm looking for an algorithm that can do: > magic(a, b, c) => ['A with property B and propery C', 'B with property X and propery Y', 'C having no properties'] I have to find for duplicates in 1000+ texts. Super performance isn't a must, but would be nice. -- Update I'm looking for sequence of words. So if: d = 'This is Product D with text engraving: "Buy". Buy it now!' The first "Buy" should not be a duplicate. I'm guessing I have to use a threshold of n words following eachother in order to be seen as duplicate.
def common_prefix_length(*args) first = args.shift (0..first.size).find_index { |i| args.any? { |a| a[i] != first[i] } } end def magic(*args) i = common_prefix_length(*args) args = args.map { |a| a[i..-1].reverse } i = common_prefix_length(*args) args.map { |a| a[i..-1].reverse } end a = "This is Product A with property B and propery C. Buy it now!" b = "This is Product B with property X and propery Y. Buy it now!" c = "This is Product C having no properties. Buy it now!" magic(a,b,c) # => ["A with property B and propery C", # "B with property X and propery Y", # "C having no properties"]
Your data sentences = [ "This is Product A with property B and propery C. Buy it now!", "This is Product B with property X and propery Y. Buy it now!", "This is Product C having no properties. Buy it now!" ] Your magic def magic(data) prefix, postfix = 0, -1 data.map{ |d| d[prefix] }.uniq.compact.size == 1 && prefix += 1 or break while true data.map{ |d| d[postfix] }.uniq.compact.size == 1 && prefix > -postfix && postfix -= 1 or break while true data.map{ |d| d[prefix..postfix] } end Your output magic(sentences) #=> [ #=> "A with property B and propery C", #=> "B with property X and propery Y", #=> "C having no properties" #=> ] Or you can use loop instead of while true def magic(data) prefix, postfix = 0, -1 loop{ data.map{ |d| d[prefix] }.uniq.compact.size == 1 && prefix += 1 or break } loop{ data.map{ |d| d[postfix] }.uniq.compact.size == 1 && prefix > -postfix && postfix -= 1 or break } data.map{ |d| d[prefix..postfix] } end
edit: this code has bugs. Just leaving my answer for reference and because i dont like it if people delete answers after being downvoted. Everyone makes mistakes :-) I liked #falsetru's approach but felt the code was unnecessarily complex. Here's my attempt: def common_prefix_length(strings) i = 0 i += 1 while strings.map{|s| s[i] }.uniq.size == 1 i end def common_suffix_length(strings) common_prefix_length(strings.map(&:reverse)) end def uncommon_infixes(strings) pl = common_prefix_length(strings) sl = common_suffix_length(strings) strings.map{|s| s[pl...-sl] } end As the OP may be concerned about performance, i did a quick benchmark: require 'fruity' require 'securerandom' prefix = 'PREFIX ' suffix = ' SUFFIX' test_data = Array.new(1000) do prefix + SecureRandom.hex + suffix end def fl00r_meth(data) prefix, postfix = 0, -1 data.map{ |d| d[prefix] }.uniq.size == 1 && prefix += 1 or break while true data.map{ |d| d[postfix] }.uniq.size == 1 && postfix -= 1 or break while true data.map{ |d| d[prefix..postfix] } end def falsetru_common_prefix_length(*args) first = args.shift (0..first.size).find_index { |i| args.any? { |a| a[i] != first[i] } } end def falsetru_meth(*args) i = falsetru_common_prefix_length(*args) args = args.map { |a| a[i..-1].reverse } i = falsetru_common_prefix_length(*args) args.map { |a| a[i..-1].reverse } end def padde_common_prefix_length(strings) i = 0 i += 1 while strings.map{|s| s[i] }.uniq.size == 1 i end def padde_common_suffix_length(strings) padde_common_prefix_length(strings.map(&:reverse)) end def padde_meth(strings) pl = padde_common_prefix_length(strings) sl = padde_common_suffix_length(strings) strings.map{|s| s[pl...-sl] } end compare do fl00r do fl00r_meth(test_data.dup) end falsetru do falsetru_meth(*test_data.dup) end padde do padde_meth(test_data.dup) end end These are the results: Running each test once. Test will take about 1 second. fl00r is similar to padde padde is faster than falsetru by 30.000000000000004% ± 10.0%