Search code examples
rubyalgorithmmathmatrixdiagonal

How to get diagonal values from specific point?


Suppose I have 10x10 matrix with the following data:

 1     2     3     4     5     6     7     8     9    10 
11    12    13    14    15    16    17    18    19    20 
21    22    23    24    25    26    27    28    29    30 
31    32    33    34    35    36    37    38    39    40 
41    42    43    44     _    46    47    48    49    50 
51    52    53    54    55    56    57    58    59    60 
61    62    63    64    65    66    67    68    69    70 
71    72    73    74    75    76    77    78    79    80 
81    82    83    84    85    86    87    88    89    90 
91    92    93    94    95    96    97    98    99   100

My position is in [4][4]. How can I list the diagonal values from this position?

For example, the expected outcome would be:

[56, 67, 78, 89, 100, 1, 12, 23, 34]
[54, 63, 72, 81, 9, 18, 27, 36]

My current solution

  def next?(index, row, size)
    (((row + index) % size) + 1 ) % size
  end

  (1...chess_size).each do |l|
    next_el, curr_el = next?(l, row, chess_size), (row + l) % chess_size

    # this gets me the first diagnonal. Note that it prints out wrong value
    tmp[0] << chess[curr_el][curr_el]

    # this gets me the values from current row below to up
    tmp[1] << chess[(row + l) % chess_size][row]
    tmp[2] << chess[-l][l]
    tmp[3] << chess[row][(row + l) % chess_size]
  end

Our matrix will always have the same number of rows and columns.


Solution

  • This is a solution to the Hackerrank Queen's attack problem.

    Code

    def count_moves(n, obs, qrow, qcol)
      qdiff = qrow-qcol
      qsum =  qrow+qcol  
      l = u = -1
      r = d =  n
      ul = qdiff >= 0 ? qrow-qcol-1 : -1
      dr = qdiff >= 0 ? n           : qrow+n-qcol
      ur = qsum < n   ? -1          : qrow-n+qcol
      dl = qsum < n   ? qrow+qcol+1 : n
      obs.uniq.each do |i,j|
        case i <=> qrow
        when -1               # up
          case j <=> qcol
          when -1               # up-left
            ul = [ul,i].max
          when 0                # up same col
            u  = [u,i].max
          when 1                # up-right
            ur = [ur,i].max
          end
        when 0                # same row
          j < qcol ? (l = [l,j].max) : r = [r,j].min
        else                  # down
          case j <=> qcol
          when -1               # down-left
            dl = [dl,i].min
          when 0                # down same col
            d  = [d,i].min
          when 1                # down-right
            dr = [dr,i].min
          end
        end
      end
      r + dl + d + dr - l - ul -u - ur - 8
    end
    

    Example

    Suppose the chess board has 9 rows and columns, with the queen's location shown by the character q and each obstruction shown with the letter o. All other locations are represented by the letter x. We see that the queen has 16 possible moves (7 up and down, 6 left and right, 1 on the up-left to down-right diagonal and 2 on the up-right to down-left diagonal.

    arr = [
      %w| x x x x x x x x x |, # 0
      %w| o x x x x x x x x |, # 1
      %w| x o x x x x x x x |, # 2
      %w| x x o x x x x x o |, # 3
      %w| x x x o x x x x x |, # 4
      %w| x x x x x x o x x |, # 5
      %w| o o x x x q x x x |, # 6
      %w| x x x x x x o x x |, # 7
      %w| x x x x x o x x x |  # 8
    #     0 1 2 3 4 5 6 7 8   
    ]
    
    qrow = qcol = nil
    obs = []
    n = arr.size
    arr.each_with_index do |a,i|
      a.each_with_index do |c,j|
        case c
        when 'o'
          obs << [i,j]
        when 'q'
          qrow=i
          qcol=j
        end
      end
    end
    

    qrow
      #=> 6
    qcol
      #=> 5
    obs
      #=> [[1, 0], [2, 1], [3, 2], [3, 8], [4, 3], [5, 6], [6, 0], [6, 1], [7, 6], [8, 5]]
    
    count_moves(n, obs, qrow, qcol)
      #=> 16
    

    Explanation

    • l is the largest column index of an obstruction in the queen's row that is less than the queen's column index;
    • r is the smalles column index of an obstruction in the queens that is greater than the queen's column index;
    • u is the largest largest row index of an obstruction in the queen's column that is less than the queen's row index;
    • d is the smallest row index of an obstruction in the queen's column that is greater than the queen's row index;
    • ul is the greatest row index of an obstruction on the queen's top-left to bottom-right diagonal that is less than the queen's row index;
    • dr is the smallest row index of an obstruction on the queen's top-left to bottom-right diagonal that is greater than the queen's row index;
    • ur is the greatest row index of an obstruction on the queen's top-right to bottom-left diagonal that is less than the queen's row index; and
    • dl is the smallest row index of an obstruction on the queen's top-right to bottom-left diagonal that is greater than the queen's row index.

    For the example above, before obstructions are taken into account, these variables are set to the following values.

    l  =  0
    r  =  9
    ul =  0 
    u  = -1
    ur =  2
    dl =  9
    d  =  9
    dr =  9
    

    Note that if the queen has row and column indices qrow and qcol,

    • i - j = qrow - qcol for all locations [i, j] on the queen's top-left to bottom- right diagonal; and
    • i + j = grow + gcol for all locations [i, j] on the queen's top-right to bottom- left diagonal

    We then loop through all (unique) obstructions, determining, for each, whether it is in the queen's row, queen's column, or one of the queen's diagonals and then replaces the value of the applicable variable with it's row or column index if it is "closer" to the queen than the previously-closest location.

    If, for example, the obstruction is in the queen's row and its column index j is less than the queen's column index, the following calculation is made:

    l = [l, j].max
    

    Similarly, if the obstruction is on the queen's top-left to bottom-right diagonal and its row index i is less than the queen's row index, the calculation would be:

    ul = [ul, i].max
    

    After all obstructions from the above example have been considered the variables have the following values.

    l  #=>  1
    r  #=>  9
    ul #=>  4
    u  #=> -1
    ur #=>  5
    dl #=>  9
    d  #=>  8
    dr #=>  7
    

    Lastly, we compute the total number of squares to which the queen may move.

    qcol - l  - 1 +  # left
    r - qcol  - 1 +  # right
    u - qrow  - 1 +  # up
    grow - d  - 1 +  # down
    ul - qrow - 1 +  # up-left
    ur - qrow - 1 +  # up-right
    qrow - dl - 1 +  # down-left
    qrow - dr - 1    # down-right
    

    which simplifies to

    r + dl + d + dr - l - ul -u - ur - 8
      #=> 9 + 9 + 8 + 7 - 1 - 4 + 1 - 5 - 8 => 16