tags:

views:

218

answers:

2

I've been slowly learning Ruby (at this point, maybe the first language I've invested any amount of time in actually learning) so this is probably going to be a very simple question for many of you.

My toy project for my studies is, basically, a roguelike. Currently, I have a Map class that contains an array of Tile objects representing, of course, each tile in the entire map. I'm attempting to create a method that will return a smaller array (most likely example would be to display the currently viewable area of the map).

My problem comes down to this. Since the array containing all these tiles is single-dimension, I can't seem to think of a clean way to slice pieces of this array out based on two x,y coordinates that the method takes to determine what to return. In other words, I can't seem to find a clean way to translate between the two coordinate pairs without some fairly ugly looking code and I get the idea that there's a very simple way to do this that is just not 'clicking'.

Ideas anyone? I'm open to some pretty crazy suggestions!

+2  A: 

You'll need to use use map on a slice of the array and map each row to a slice itself.

a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a[1..2].map { |row| row[0..1] }

=> [["d", "e"], ["g", "h"]]

Kevin Peterson
I was going to suggest something exactly like this, but you beat me to it. A two-dimensional array needs to have a nested Array#map call, that's all, though it might seem rather unintuitive for people unfamiliar with how Ruby can chain and nest blocks.
tadman
Not exactly what I was looking for (since I'm working with single-dimension arrays only). However, thanks for the exposure to some more of the Array class's capabilities!
Paige Aran
+4  A: 

If your array is single dimention you can map x and y coords to an array index much like you do pixels on a vga buffer.

offset = y * buffer_width + x

If your map tile width is 100 tiles and you wanted to get tile 5,5 then 5 * 100 + 5 = array index 505 would be the corresponding tile in your 1 dimensional array.

You can use this to piece together a viewable region on screen. For instance a 10x10 viewable tiles: Start at your offset and grab the next 10 items, add buffer_width to drop a row and add the next 10, and so on until you have all 10 rows for your 10x10 viewable area.

Here is an example on a smaller tile set with a tile buffer width of 5 and selecting a viewable 3x3 area:

buffer = ['0,0', '1,0', '2,0', '3,0', '4,0',
          '0,1', '1,1', '2,1', '3,1', '4,1',
          '0,2', '1,2', '2,2', '3,2', '4,2',
          '0,3', '1,3', '2,3', '3,3', '4,3',
          '0,4', '1,4', '2,4', '3,4', '4,4']

buffer_width = 5
buffer_height = 5

# now lets say we want to grab a 3x3 slice from right 
# in the middle of the array from 1,1->3,3

x1,y1 = 1,1
x2,y2 = 3,3

view_width = x2 - x1
view_height = y2 - y1

(0..view_height).each do |row|
  offset = (y1 + row) * buffer_width + x1
  puts buffer[offset..offset+view_width].inspect
end

Our result will be an output like this:

["1,1", "2,1", "3,1"]
["1,2", "2,2", "3,2"]
["1,3", "2,3", "3,3"]

Which you can string together in a new single dimensional or multi dimensional, which ever you see fit.

Hope this helps.

Corban Brook
Perfect, that's a lot cleaner than much of what I was coming up with. Thanks!
Paige Aran