0

I'm implementing gomoku game in Ruby, this is a variation of tic-tac-toe played on 15x15 board, and the first player who places 5 O's or X's in horizontal, vertical or diagonal row wins.

First, I assigning Matrix to a variable and fill it with numbers from 0 to 224, so there are no repetitions and I could count them later

gomoku = Matrix.zero(15)
num = 0
15.times do |i|
  15.times do |j|
    gomoku[i, j] = num
    num += 1
  end
end

then players take turns, and after every turn I check a win with the method win?

def win? matrix
  15.times do |i|
    return true if matrix.row_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4 # thanks to sawa for this way of counting adjacent duplicates
    return true if matrix.column_vectors[i].chunk{|e| e}.map{|_, v| v.length}.max > 4
  end
  return false
end

I know, that I'm probably doing it wrong, but my problem isn't that, though suggestions are welcome. The problem is with diagonal rows. I don't know how to count duplicates in diagonal rows

basedtho
  • 457
  • 1
  • 5
  • 11
  • `Matrix` is immutable, you shouldn't be able to do `gomoku[i, j] = num`... – Amadan Sep 08 '14 at 06:52
  • You are wrong, I just tried it – basedtho Sep 08 '14 at 07:01
  • If you are using [standard Ruby `Matrix`](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/matrix/rdoc/Matrix.html#method-i-clone): "...There should be no good reason to do this since *Matrices are immutable*." Also note how `Matrix#[]=` does not exist. `ruby -r matrix -e "a = Matrix.zero(2); a[0, 0] = 1"` will return `private method `[]=' called for Matrix[[0, 0], [0, 0]]:Matrix (NoMethodError)`. If, on the other hand, you are using a library, you might have wanted to point it out. – Amadan Sep 08 '14 at 07:33
  • I'm sorry, I didn't know that I'm using modified version of Matrix, I'm doing this in a rails application, and am not aware of all of its features. – basedtho Sep 08 '14 at 07:41

1 Answers1

1
diagonal_vectors = (-10 .. 10).flat_map do |x|
  i = x < 0 ? 0 : x
  j = x < 0 ? -x : 0
  d = 15 - x.abs
  [
    d.times.map { |k|
      gomoku[i + k, j + k]
    },
    d.times.map { |k|
      gomoku[i + k, 14 - j - k]
    }
  ]
end

With this, you can apply the same test sawa gave you.

EDIT: What this does

When looking at diagonals, there's two kinds: going down-left, and going down-right. Let's focus on down-right ones for now. In a 15x15 matrix, there are 29 down-right diagonals: one starting at each element of the first row, one starting at each element of the first column, but taking care not to count the one starting at [0, 0] twice. But some diagonals are too short, so we want to only take those that start on the first eleven rows and columns (because others will be shorter than 5 elements). This is what the first three lines do: [i, j] will be [10, 0], [9, 0] ... [0, 0], [0, 1], ... [0, 10]. d is the length of a diagonal starting at that position. Then, d.times.map { |k| gomoku[i + k, j + k] } collects all the elements in that diagonal. Say we're working on [10, 0]: d is 5, so we have [10, 0], [11, 1], [12, 2], [13, 3], [14, 4]; and we collect values at those coordinates in a list. Simultaneously, we'll also work on a down-left diagonal; that's the other map's job, which flips one coordinate. Thus, the inner block will return a two-element array, which is two diagonals, one down-left, one down-right. flat_map will take care of iterating while squishing the two-element arrays so that we get one big array of diagonals, not array of two-element arrays of diagonals.

Amadan
  • 191,408
  • 23
  • 240
  • 301