Recursive method to find matching rows on grid - ruby

I have a grid object which is populated by space objects each containing a value. What I want to do is write a method which takes a space, and a number representing the amount of equal spaces I would like to find, to see if there is anywhere on the grid with x-number of equal spaces in the same direction.
In psuedo-code, the steps I planned are:
Begin at the top-left corner of the grid.
Check each direction (N,S,E,W,NE,NW,SE,SW) for 2 conditions
The space in that direction exists
The value in that space matches the value in the current space
If these conditions all fail I want call the method again, but with the current space as the next space (increasing left >> right & top >> bottom)
If these conditions are satisfied I want to store this value in a list and call the method again to check the matching space only in the direction the current space was found
If the next space finds that its adjacent space does not match, I want to
Quit searching
Return to the first space the method was called with
Check the remaining directions to satify the 2 conditions
Call the method again with the next space if no spaces satisfy (increasing left >> right & top >> bottom)
The function returns when the current space is the bottom right space or the list of matching values is the same length as the value specified
If this were implemented on a tic-tac-toe board like below, I would start at the top left "O". I would check North, fail, check North-East, fail, check East, pass. Once it passes I would move to the next "O" and check only to its East since the current "O" was found to the east of its previous space, which would give me the expected result of finding 3 "O"s.
The other case I want to handle is, if the South-East were checked the space would move to the center, where the center space would check its South-East, notice that "X" does not equal "O", return back to the top-left space, check the remaining directions, then move to the next space down the line and repeat until all spaces are checked or a match is found
My non-recursive implementation of this method is as follows. It loops through each space, and for each space loops through each compass direction and assigns the value repeated 'num' times to the found variable which will return nil if nothing was found, or the value which was repeated 'num' times
def check_for_row_of(num)
found = nil
each_space do |space|
COMPASS.each do |direction|
if num_spaces_in_direction(space.pos, direction) == num
values = get_spaces_by_pattern(space.pos, direction)
found = values.uniq[0] if values.uniq.length == 1 && values.uniq[0] != " "
end
end
end
found
end

If I understood you correctly, I believe the following code answers your question. Please, tell me if it isn't quite what you were thinking.
For ease of reading, I kept the recursive search function separate from the (somewhat clunky) logic that iterates over each cell and direction.
Also, it is worth mentioning that I am calling transpose and reverse on the Array of Arrays so that the grid is easier for humans to read. Lower left is (0, 0) and top right is (2, 2).
def search(grid, x, y, direction, length_to_find, value_to_find=nil)
return true if length_to_find == 0
value_at_location = grid.transpose[x].reverse[y] if grid.transpose[x]
value_to_find ||= value_at_location
return false if value_at_location != value_to_find
y += 1 if direction =~ /N/i
y -= 1 if direction =~ /S/i
x += 1 if direction =~ /E/i
x -= 1 if direction =~ /W/i
search(grid, x, y, direction, length_to_find - 1, value_to_find)
end
def row_of?(grid, length_to_find)
grid.length.times do |row|
grid[0].length.times do |col|
%w[N NE E SE S SW W NW].each do |direction|
if search(grid, row, col, direction, length_to_find)
return true
end
end
end
end
false
end
grid = [
[ "O", "O", "O"],
[ " ", "O", "X"],
[ " ", "X", "X"]
]
puts row_of?(grid, 3)

Related

ruby game with arrays and loops

Use the river variable that defines the river (see code below).
-: clear water.
C: crocodile.
The first five characters represent the first part of the river.
The second five characters represent the second part of the
river.
Each turn:
Check to see if the player is in the same position as a
crocodile. If they are, puts You were eaten.' and stop the program. putsthe whole river. Include aPwhere the player is. Ask the player if they want to go to left, right or neither. The player entersleft, rightorneither. Make the player float down the river by one river part (one line of digits). Move them to the left, the right, or keep them where they are. Print You survived!` if the player makes it past all parts of the
river without hitting a crocodile.
To stop the game when the user is eaten or survives the
whole river, don't use exit to quit the program because this
will break the automated tests. To exit a while loop early, use
the break keyword.
river = "--P--,--C--,CC-CC,CC-CC"
river1 = river.split(",")
x = 0
y = 2
loop do
puts river1
puts "Type left, right or neither"
input = gets.chomp
if input == "left"
river1[x][y] = "-"
river1[(x += 1)][(y -= 1)] = "P"
elsif input == "right"
river1[x][y] = "-"
river1[(x += 1)][(y -= 1)] = "P"
elsif input == "neither"
puts "You were eaten."
break
end
end
that's the first step which is working but i have no idea how to jump to another parts of the river in the loop. Im totally stuck

Creating a checkerboard using Ruby and "\n" not disappearing

I feel as if I am close to a solution and have been tinkering around with this as a newb for some time. Why, for some reason, are my "\n"'s not disappearing when outputted for "next line" and the output has unneeded white space?
Task: Write a function which takes one parameter representing the dimensions of a checkered board. The board will always be square, so 5 means you will need a 5x5 board.
The dark squares will be represented by a unicode white square, while the light squares will be represented by a unicode black square (the opposite colors ensure the board doesn't look reversed on code wars' dark background). It should return a string of the board with a space in between each square and taking into account new lines.
An even number should return a board that begins with a dark square. An odd number should return a board that begins with a light square.
The input is expected to be a whole number that's at least two, and returns false otherwise (Nothing in Haskell).
I am close, and here is what I have so far:
def checkered_board(dimension)
black = "\u25A1 "
white = "\u25A0 "
checkboard = nil
checker_array = []
if dimension < 2 or dimension.is_a? String
return false
else
count = dimension
while count <= dimension && count > 0
if count % 2 == 0
checkboard = ("\u25A1 \u25A0" + "\n")
checker_array << checkboard
count -= 1
else
checkboard = ("\u25A0 \u25A1" + "\n")
checker_array << checkboard
count -= 1
end
end
end
checkboard = checker_array.join(" ")
p checkboard
end
Here is the TDD specs:
Test.assert_equals(checkered_board(0), false)
Test.assert_equals(checkered_board(2), "\u25A1 \u25A0\n\u25A0 \u25A1")
Note: Hidden specs demonstrate that it should respond with false if dimension is not an integer. .is_a? String and .is_a? Integer is not working for me too.
Output appears like so, and is not appearing even:
□ ■
■ □
Thanks for any and all help :).
Try changing:
if dimension < 2 or dimension.is_a? String
to
if !dimension.is_a?(Integer) || dimension < 2
The left most test will be done first. At the moment, if dimension is a String, it is first compared with 2 - which will raise an error - before it is tested as to whether it is a String. You need to check the type of object before you compare it with another object.
Also, I think the check should be whether dimension is not an Integer, rather than whether it is a String. For example, in your original code, what would happen if dimension was an Array?
The join method will concatenate the elements with a space character inserted between them. So this line from the program:
checkboard = checker_array.join(" ")
will result in this string:
"\u25A1 \u25A0\n \u25A0 \u25A1"
Omitting the argument to join should produce the expected output, ie.:
checkboard = checker_array.join
Refer to the documentation on the Array join method.

Algorithm: path finding with variable path width

given a grid of paths with different width, how can i find a path which leads to the end point?
The path is going to be represented by a two dimentional array where 0 means cannot be walk on, 1 means it is walkable, 2 represents starting point and 3 represents end point. Consider the following example:
21111111100000
00000011000000
00001111111111
00001111100111
00001110000101
00001111100113
in the above example the width of a path varies from 1 to 3, and there exists many solutions which would lead to the end point. I want to find one path which leads to it and the path does not have to be the shortest one (should not be the longest one either). The width of each path is unknown which means the grid could be all "1"s except the starting and end point.
Edited: The path should not contain uneccessary "wasted" walk meaning that if a vertical path has width 2 the result should not just walk down the path and then take one step right then walk all the way up
I agree with Calumn: DFS is the simplest approach here. Here is a simple solution in python-like pseudocode. It will print the solution as a sequence of 'L','R',U','D' to indicate left,right,up, or down.
def flood(x,y,story):
if (visited[x][y] or map[x][y]=='0'): return;
visited[x][y]=True;
if (map[x][y]=='3'):
print 'done. The path is: '+story
return
if (x<len(a[0])): flood(x+1,y,story+'R')
if (y<len(a)): flood(x,y+1,story+'D')
if (x>0): flood(x-1,y,story+'L')
if (y>0): flood(x,y-1,story+'U')
def solve(map):
visited = array_of_false_of_same_size_as(map)
x,y = find_the_two(map)
flood(x,y,'')
The optimization of making it stop as soon as it finds a solution is left as an exercise to the reader (you could make flood return a boolean to indicate if it found something, or use a global flag).
(p.s. I made this answer community wiki since I'm just clarifying Calumn's answer. I can't claim much credit)
Breadth-First Search version, also in Python
For what it's worth, and just to show that breadth-first search is not that complicated, an actual runnable program in Python:
def find(grid, xstart=0, ystart=0):
# Maps (xi,yi) to (x(i-1), y(i-1))
prev = {(xstart, ystart):None}
# Prepare for the breadth-first search
queue = [(xstart, ystart)]
qpos = 0
# Possibly enqueue a trial coordinate
def enqueue(prevxy, dx, dy):
x = prevxy[0] + dx
y = prevxy[1] + dy
xy = (x, y)
# Check that it hasn't been visited and the coordinates
# are valid and the grid position is not a 0
if (xy not in prev
and x >= 0 and x < len(grid)
and y >= 0 and y < len(grid[x])
and grid[x][y] != 0):
# Record the history (and the fact that we've been here)
prev[xy] = prevxy
# If we found the target, signal success
if grid[x][y] == 3:
return xy
# Otherwise, queue the new coordinates
else:
queue.append(xy)
return None
# The actual breadth-first search
while qpos < len(queue):
xy = queue[qpos]
qpos += 1
found = ( enqueue(xy, 1, 0)
or enqueue(xy, 0, 1)
or enqueue(xy, -1, 0)
or enqueue(xy, 0, -1))
if found: break
# Recover the path
path = []
while found:
path.append(found)
found = prev[found]
path.reverse()
return path
# Test run
grid = [ [2,1,1,1,1,1,1,1,1,0,0,0,0,0]
, [0,0,0,0,0,0,1,1,0,0,0,0,0,0]
, [0,0,0,0,1,1,1,1,1,1,1,1,1,1]
, [0,0,0,0,1,1,1,1,1,0,0,1,1,1]
, [0,0,0,0,1,1,1,0,0,0,0,1,0,1]
, [0,0,0,0,1,1,1,1,1,0,0,1,1,3]
]
for x, y in find(grid): grid[x][y]='*'
print '\n'.join(''.join(str(p) for p in line) for line in grid)
Output:
*******1100000
000000*1000000
000011******11
00001111100*11
00001110000*01
00001111100***

Finding the indexes of specific strings in an array, using a differently ordered equivalent array, ruby

I have two arrays: fasta_ids & frags_by_density. Both contain the same set of ≈1300 strings.
fasta_ids is ordered numerically e.g. ['frag1', 'frag2', 'frag3'...]
frags_by_density contains the same strings ordered differently e.g. ['frag14', 'frag1000'...]
The way in which frag_by_density is ordered is irrelevant to the question (but for any bioinformaticians, the 'frags' are contigs ordered by snp density).
What I want to do is find the indexes in the frag_by_density array, that contain each of the strings in fasta_ids. I want to end up with a new array of those positions (indexes), which will be in the same order as the fasta_ids array.
For example, if the order of the 'frag' strings was identical in both the fasta_ids and frags_by_density arrays, the output array would be: [0, 1, 2, 3...].
In this example, the value at index 2 of the output array (2), corresponds to the value at index 2 of fasta_ids ('frag3') - so I can deduce from this that the 'frag3' string is at index 2 in frags_by_density.
Below is the code I have come up with, at the moment it gets stuck in what I think is an infinite loop. I have annotated what each part should do:
x = 0 #the value of x will represent the position (index) in the density array
position_each_frag_id_in_d = [] #want to get positions of the values in frag_ids in frags_by_density
iteration = []
fasta_ids.each do |i|
if frags_by_density[x] == i
position_each_frag_id_in_d << x #if the value at position x matches the value at i, add it to the new array
iteration << i
else
until frags_by_density[x] == i #otherwise increment x until they do match, and add the position
x +=1
end
position_each_frag_id_in_d << x
iteration << i
end
x = iteration.length # x should be incremented, however I cannot simply do: x += 1, as x may have been incremented by the until loop
end
puts position_each_frag_id_in_d
This was quite a complex question to put into words. Hopefully there is a much easier solution, or at least someone can modify what I have started.
Update: renamed the array fasta_ids, as it is in the code (sorry if any confusion)
fasta_id = frag_id
Non optimized version. array.index(x) returns index of x in array or nil if not found. compact then removes nil elements from the array.
position_of_frag_id_in_d = frag_ids.map{|x| frag_by_density.index(x)}.compact

How do I modify the Damerau-Levenshtein algorithm, such that it also includes the start index, and the end index of the larger substring?

Here is my code:
#http://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
# used for fuzzy matching of two strings
# for indexing, seq2 must be the parent string
def dameraulevenshtein(seq1, seq2)
oneago = nil
min = 100000000000 #index
max = 0 #index
thisrow = (1..seq2.size).to_a + [0]
seq1.size.times do |x|
twoago, oneago, thisrow = oneago, thisrow, [0] * seq2.size + [x + 1]
seq2.size.times do |y|
delcost = oneago[y] + 1
addcost = thisrow[y - 1] + 1
subcost = oneago[y - 1] + ((seq1[x] != seq2[y]) ? 1 : 0)
thisrow[y] = [delcost, addcost, subcost].min
if (x > 0 and y > 0 and seq1[x] == seq2[y-1] and seq1[x-1] == seq2[y] and seq1[x] != seq2[y])
thisrow[y] = [thisrow[y], twoago[y-2] + 1].min
end
end
end
return thisrow[seq2.size - 1], min, max
end
there has to be someway to get the starting and ending index of substring, seq1, withing parent string, seq2, right?
I'm not entirely sure how this algorithm works, even after reading the wiki article on it. I mean, I understand the highest level explanation, as it finds the insertion, deletion, and transposition difference (the lines in the second loop).. but beyond that. I'm a bit lost.
Here is an example of something that I wan to be able to do with this (^):
substring = "hello there"
search_string = "uh,\n\thello\n\t there"
the indexes should be:
start: 5
end: 18 (last char of string)
Ideally, the search_string will never be modified. But, I guess I could take out all the white space characters (since there are only.. 3? \n \r and \t) store the indexes of each white space character, get the indexes of my substring, and then re-add in the white space characters, making sure to compensate the substring's indexes as I offset them with the white space characters that were originally in there in the first place. -- but if this could all be done in the same method, that would be amazing, as the algorithm is already O(n^2).. =(
At some point, I'd like to only allow white space characters to split up the substring (s1).. but one thing at a time
I don't think this algorithm is the right choice for what you want to do. The algorithm is simply calculating the distance between two strings in terms of the number of modifications you need to make to turn one string into another. If we rename your function to dlmatch for brevity and only return the distance, then we have:
dlmatch("hello there", "uh, \n\thello\n\t there"
=> 7
meaning that you can convert one string into the other in 7 steps (effectively by removing seven characters from the second). The problem is that 7 steps is a pretty big difference:
dlmatch("hello there", "panda here"
=> 6
This would actually imply that "hello there" and "panda here" are closer matches than the first example.
If what you are trying to do is "find a substring that mostly matches", I think you are stuck with an O(n^3) algorithm as you feed the first string to a series of substrings of the second string, and then selecting the substring that provides you the closest match.
Alternatively, you may be better off trying to do pre-processing on the search string and then doing regexp matching with the substring. For example, you could strip off all special characters and then build a regexp that looks for words in the substring that are case insensitive and can have any amount of whitespace between them.

Resources