8

I'm trying to create a three-dimensional array which contains blocks (like a rubiks-cube).

I tried many things but I can't get it to work.

func generateTiles(x int, y int, z int) [][][]*tile{
  var tiles [][][]*tile

  // Something here
  // resulting in a x by y by z array
  // filled with *tile

  return tiles
}

Any suggestions?

Patrick
  • 489
  • 2
  • 8
  • 12

4 Answers4

17

You have to initialize each layer on its own. Example (on play):

tiles = make([][][]*tile, x)

for i := range tiles {
    tiles[i] = make([][]*tile, y)
    for j := range tiles[i] {
        tiles[i][j] = make([]*tile, z)
    }
}
nemo
  • 55,207
  • 13
  • 135
  • 135
8

I'd personally use a 1D slice for performance reasons, I'm adding this as an alternative:

type Tile struct {
    x, y, z int
}

type Tiles struct {
    t       []*Tile
    w, h, d int
}

func New(w, h, d int) *Tiles {
    return &Tiles{
        t: make([]*Tile, w*h*d),
        w: w,
        h: h,
        d: d,
    }
}

// indexing based on http://stackoverflow.com/a/20266350/145587
func (t *Tiles) At(x, y, z int) *Tile {
    idx := t.h*t.w*z + t.w*y
    return t.t[idx+x]
}

func (t *Tiles) Set(x, y, z int, val *Tile) {
    idx := t.h*t.w*z + t.w*y
    t.t[idx+x] = val
}

func fillTiles(w int, h int, d int) *Tiles {
    tiles := New(w, h, d)

    for x := 0; x < w; x++ {
        for y := 0; y < h; y++ {
            for z := 0; z < d; z++ {
                tiles.Set(x, y, z, &Tile{x, y, z})
            }
        }
    }

    return tiles
}

playground

OneOfOne
  • 95,033
  • 20
  • 184
  • 185
  • I haven't though of this approach before. Thanks! I'll try this one too. Can you explain how exactly it will save on the performance? – Patrick Nov 29 '15 at 15:09
  • 2
    @Patrick well for a few reasons, 1. It's a one big block of contiguous memory instead of multiple fragmented blocks. 2. Only one bounds check per access instead of three (slice[xyz] vs slice[x][y][z]), which can add up if you're looking for high performance. – OneOfOne Nov 29 '15 at 16:18
0

There is an example for creating a two dimensional array on GoByExample: https://gobyexample.com/arrays. You should be able to expand that into a three dimensional case.

Here is what I came up with.

CODE

package main

import (
    "fmt"
)

type Tile struct {
    value int
}

func create3D( x, y, z int) [][][]*Tile {
    result := make([][][]*Tile,x)
    for i := 0 ; i < x ; i++ {
        result[i] = make([][]*Tile,y);
        for j := 0; j < y; j++ {
            result[i][j] = make([]*Tile,z);
            for k := 0 ; k < z; k++ {
                result[i][j][k] = new(Tile)
                result[i][j][k].value = i + j + k;
            }
        }
    }
    return result
}

func main() {
    X := 3
    Y := 4
    Z := 5

    mat := create3D( X , Y , Z);
    for i := 0; i < X; i++ {
        for j := 0 ; j < Y; j++ {
            for k := 0 ; k < Z; k++ {
                fmt.Printf("%d ",mat[i][j][k].value)
            }
            fmt.Println();
        }
        fmt.Println();
    }

}
deathly809
  • 384
  • 4
  • 11
-1

It works like this, but to me this feels very inefficient. Using the append-operation this many times. And it feels bloated, this should be possible in a simpler way.

func generateTiles(x int, y int, z int) [][][]*tile {
    var tiles [][][]*tile

    for i := 0; i < z; i++ {
        var layer [][]*tile
        for j := 0; j < y; j++ {
            var row []*tile
            for k := 0; k < x; k++ {
                var t *tile
                t = &tile{}
                row = append(row, t)
                count++
            }
            layer = append(layer, row)
        }
        tiles = append(tiles, layer)
    }

    return tiles
}
Patrick
  • 489
  • 2
  • 8
  • 12