Implementing two-dimensional vector syntax to access a 1D vector?

I am creating a roguelike toy and have a structure Levelfor storing a game map for which the most naive implementation is a 2D vector.

I follow this tutorial , which uses Vectorof Vectors, but claims that to improve performance it is also possible to use a single Vectorsize MAP_HEIGHT * MAP_WIDTH, and to access a fragment in (x, y)one can simply access map[y * MAP_WIDTH + x].

I am trying to implement this faster method, but using getters and setters is inconvenient, and public fields are not that good either. I would prefer it to be like a 2D vector.

To do this, I need to implement a sign Indexfor my class, but I'm not sure how to get the result I want. Perhaps nested impls? I really do not know.

Here is my code with a terrible attempt to implement Indexfor my structure, which obviously will not work for my purposes, because it is one-dimensional:

const MAP_WIDTH: i32 = 80;
const MAP_HEIGHT: i32 = 45;

pub struct Level {
    map: Vec<Tile>,
}

impl Level {
    pub fn new() -> Self {
        Level { map: vec![Tile::empty(); (MAP_HEIGHT * MAP_WIDTH) as usize] }
    }
}

impl std::ops::Index<i32> for Level {
    type Output = Tile;
    fn index(&self, x: i32) -> &Self::Output {
        self[MAP_WIDTH + x]; // We have x and y values; how do we make this work?
    }
}
+4
source share
3 answers

Perhaps, although not obvious.

First of all, I suggest having MAP_WIDTHand MAP_HEIGHTin usize, since they are positive integers:

const MAP_WIDTH: usize = 80;
const MAP_HEIGHT: usize = 45;

Then you need to implement Index(and possibly IndexMut) to return the slice; in this case, I assume that you want the first coordinate to be a string:

impl std::ops::Index<usize> for Level {
    type Output = [Tile];

    fn index(&self, row: usize) -> &[Tile] {
        let start = MAP_WIDTH * row;
        &self.map[start .. start + MAP_WIDTH]
    }
}

impl std::ops::IndexMut<usize> for Level {
    fn index_mut(&mut self, row: usize) -> &mut [Tile] {
        let start = MAP_WIDTH * row;
        &mut self.map[start .. start + MAP_WIDTH]
    }
}

, a Level, ; .

Tile:

const MAP_WIDTH: usize = 80;
const MAP_HEIGHT: usize = 45;

#[derive(Clone, Debug)]
pub struct Tile {
    x: u32,
    y: u32
}

pub struct Level {
    map: Vec<Tile>,
}

impl Level {
    pub fn new() -> Self {
        Level { map: vec![Tile { x: 0, y: 0 }; (MAP_HEIGHT * MAP_WIDTH) as usize] }
    }
}

impl std::ops::Index<usize> for Level {
    type Output = [Tile];

    fn index(&self, row: usize) -> &[Tile] {
        let start = MAP_WIDTH * row;
        &self.map[start .. start + MAP_WIDTH]
    }
}

impl std::ops::IndexMut<usize> for Level {
    fn index_mut(&mut self, row: usize) -> &mut [Tile] {
        let start = MAP_WIDTH * row;
        &mut self.map[start .. start + MAP_WIDTH]
    }
}

fn main() {
    let mut lvl = Level::new(); 

    lvl[5][2] = Tile { x: 5, y: 2 };

    println!("{:?}", lvl[5][2]); // Tile { x: 5, y: 2 }
}
+5

(i32, i32).

type Pos = (i32, i32);

impl std::ops::Index<Pos> for Level {
    type Output = Tile;
    fn index(&self, (x, y): Pos) -> &Self::Output {
        &self.map[(y * MAP_WIDTH + x) as usize]
    }
}

, :

let tile = level[(3, 4)];

i32, , usize, Vec. , u32 usize . x y , . , (0, 0).

+4

, . Index :

pub trait Index<Idx> 
where
    Idx: ?Sized, 
{
    type Output: ?Sized;
    fn index(&self, index: Idx) -> &Self::Output;
}

game[x][y], game[x] :

  • -. (&Self::Output)
  • Index.

, self, self Index usize, .

:

impl std::ops::Index<(usize, usize)> for Level {
    type Output = Tile;
    fn index(&self, (x, y): (usize, usize)) -> &Self::Output {
        &self.map[MAP_WIDTH as usize * y + x]
    }
} 

level[(43, 12)].


Index , , , , , . , "" , ​​ HashMap, &[Tile]. &[Tile] API . , , , , .

+1
source

Source: https://habr.com/ru/post/1686818/


All Articles