Więcej niż jeden wskaźnik mutowalny

0

Witam
Napisałem strukturę Rectangle która ma metode update która przyjmuje &mut self oraz &vec<Rectangle>. Chodzi o to że muszę po tym jak wyciągnę referencje mutowalną potrzebuje jeszcze referencji do vectora<Rectangle> i wtedy jest problem z tym checker borrowem. Pisałem wcześniej dużo rzeczy obiektowo. Może da się to innaczej rozgryźć lub obejsć jakimiś metodami która ma już zaimplementowane RUST ale nic nie moge znaleźc bądz jestem słaby w szukaniu w necie.

use std::{default, cell::RefCell, rc::Rc, fmt::Display, borrow::BorrowMut, sync::Arc};
use macroquad::prelude::*;
use rand::{*, distributions::{Uniform, uniform::SampleBorrow}};

extern crate rand;

trait Draw {
    fn draw(&self);
}

#[derive(Default)]
struct Rectangle {
    position: Vec2,
    size: Vec2,
    directionHorizontal: bool,
    directionVertical: bool, 
    color: Color,
}


impl Rectangle {
    fn new(position:Vec2, size:Vec2, directionHorizontal: bool, directionVertical: bool, color:Color) -> Self{
        Self { position, size, directionHorizontal, directionVertical, color}
    }
    fn update(&mut self,vec:&Vec<Rectangle>){
        if self.directionHorizontal {
            for rect in vec {
                if Game::collisionR(self, rect, 1., 0.) {
                    self.directionHorizontal = false;
                    break;
                }
            }
            if self.directionHorizontal{
                self.position.x += 1.;
            }
        }
        else {
    
        }
        if self.directionVertical {
    
        }
        else {
    
        }
        
    }
    fn draw(&self) {
        draw_rectangle(self.position.x, self.position.y, self.size.x, self.size.y, self.color);
    }
}


#[derive(Default)]
struct Game {
    WIDTH_CANVAS:u32,
    HEIGHT_CANVAS:u32
}


impl Game{
    fn new(counts_rects: i32, WIDTH_CANVAS:u32, HEIGHT_CANVAS:u32, range_size:Vec2,rectangles: &mut Vec<Rectangle>) -> Self {
        let mut rng = rand::thread_rng();

        for i in 0..counts_rects {
            loop {
                let size_rect = rng.sample(Uniform::from(range_size.x..range_size.y));
                let size = Vec2 {x: size_rect, y: size_rect };

                let temp = Rectangle {
                    position: Vec2 { x: rng.sample(Uniform::from(0..WIDTH_CANVAS-size.x as u32)) as f32, y: rng.sample(Uniform::from(0..HEIGHT_CANVAS-size.y as u32)) as f32},
                    size,
                    directionHorizontal: random(),
                    directionVertical: random(),
                    color: Color::from_rgba(random(),random(),random(),255),
                };

                let mut find_collision = false;
                for index in rectangles.iter_mut() {
                    if Self::collision(&index, &temp){
                        find_collision = true;
                        break;
                    } 
                }
                if rectangles.len() == 0 || !find_collision{
                    rectangles.push(temp);
                    break;
                }
            }
        }
        

        Self {WIDTH_CANVAS,HEIGHT_CANVAS}
    }

    fn collision(r1:& Rectangle, r2:& Rectangle) -> bool {
        if 
            r1.position.x + r1.size.x > r2.position.x && 
            r1.position.x < r2.position.x + r2.size.x&& 
            r1.position.y + r1.size.y > r2.position.y && 
            r1.position.y < r2.position.y + r2.size.y  {
                return true;
            }

        return false;
    }
    
    fn collisionR(r1:& Rectangle, r2:& Rectangle, shift_x:f32, shift_y:f32) -> bool {
        if 
            r1.position.x + r1.size.x > r2.position.x && 
            r1.position.x < r2.position.x + r2.size.x&& 
            r1.position.y + r1.size.y > r2.position.y && 
            r1.position.y < r2.position.y + r2.size.y  {
            return true;
        }

        return false;
    }

   
}

#[macroquad::main("Arkanoid")]
async fn main() {
    let mut vec:Vec<Rectangle> = Vec::new();
    let mut game = Game::new(2000, 800, 600, Vec2 {x:1.,y:50.}, &mut vec);

    loop {
        clear_background(BLACK);


        for i in vec.iter_mut() {
            i.update(&vec)
        }
        //TUTAJ POWYŻEJ JEST BŁĄD NIE POTRAWIE TEGO NAPRAWIĆ// 


        for i in vec.iter() {
            i.draw();
        }
        next_frame().await
    }
}

3

Rust miejscami jest zbyt restrykcyjny, ale w tym przypadku nie powinieneś narzekać, bo pokazuje ci potencjalne problemy.

Pomyśl, co robisz - przechodzisz przez listę obiektów i odpalasz metodę update dla każdego obiektu (w zasadzie dla każdej struktury mówiąc po rustowemu). Ta metoda odpala wewnętrzną pętlę, która chodzi po tej samej liście obiektów i odpala Game::collisionR (który sprawdza ich właściwość position), a następnie zmienia w miejscu obiekt przez self.position.x += 1.. Podobnie z self.directionHorizontal. Czyli jednocześnie zmieniasz to w locie, jak i sprawdzasz po tym. To jest potencjalnie bugogenne. W innych językach by ci się skompilowało, ale potem byś potencjalnie tracił czas na szukaniu buga związanego z tym, że niektóre obiekty po drodze się zmienią.

Ja bym spróbował oddzielić fazę sprawdzania kolizji od fazy mutowania np. tak (przykład od zera, ale podobny do twojego problemu):

#[derive(Debug)]
struct Foo {
    v: i32,
    duplicate: bool,
}

impl Foo {
    fn new(v: i32) -> Foo {
        Foo { v, duplicate: false }
    }
    // odpowiednik twojej metody update
    fn mark_duplicate(&mut self){ 
       self.duplicate = true;
    }
    // odpowiednik twojej metody Game::collisionR
    fn check_duplicate(&self, other: &Foo) -> bool {
        self.v == other.v
    }
}


fn main() {
  let mut vec: Vec<Foo> = vec![
     Foo::new(1), Foo::new(2), Foo::new(3), Foo::new(2), 
  ];
  
  let mut duplicates = vec![]; 
  // faza sprawdzania 
  for (i, a) in vec.iter().enumerate() {
    for (j, b) in vec.iter().enumerate() {
        if i != j && a.check_duplicate(b) {
            duplicates.push(i); // dodajemy indeks znalezionego duplikatu
        }
    }
  }
  // faza mutacji
  for idx in &duplicates {
      vec[*idx].mark_duplicate();
  }
  println!("{:?}", vec);
 
}

Uwaga poboczna: nie wiem, czy vec to dobra nazwa, bo istnieje makro vec!. Ale z drugiej strony - kompiluje się, więc widocznie Rust nie widzi problemu.

Druga uwaga poboczna. Zamiast iterować po całym wektorze dla każdego obiektu (złożoność algorytmiczna O(n^2)), możesz iterować tylko po tych, co zostały niesprawdzone:

  for i in 0..vec.len() - 1 {
    for j in i + 1..vec.len() {
        let a = &vec[i];    
        let b = &vec[j];
        if a.check_duplicate(b) {
            duplicates.push(i);
            duplicates.push(j);
        }
    }
  }
  

co da mniejszą złożoność algorytmiczną (O(n^2 / 2 - 1/2n))

0

Mały hint, praktycznie nigdy nie ma sensu brać &Vec<Data>, zamiast tego lepiej brać &[Data] (większa elastyczność).

0

Najgorsze jest to że chatgpt i bing od windowsa robią dokładnie ten sam błąd nie i porafią tego naprawić .

0
plugan300 napisał(a):

Najgorsze jest to że chatgpt i bing od windowsa robią dokładnie ten sam błąd nie i porafią tego naprawić .

ChatGPT to nie jest (jeszcze) dobre narzędzie do naprawy takich rzeczy, także nie wiem jaki jest sens je wyciągać tutaj.

0

Nie traktowałbym ChatGPT jako narzędzia do wypluwania czy naprawiania kodu. Chociaż dobry jest do nauki, ale trzeba go umieć prowadzić i zadawać pomocnicze pytania. To bardziej jakbyś miał rozmowę z kimś o większej wiedzy niż twoja, ale kimś, kto niekoniecznie zrozumie twoje intencje czy zna cały kontekst.

Trochę tak jakbyś gadał z doświadczonym programistą, który by ci tłumaczył i pisał kod długopisem na kartce. Możesz się nauczyć, zainspirować, ale nie tak, że da ci pełne rozwiązanie.

0
LukeJL napisał(a):

Nie traktowałbym ChatGPT jako narzędzia do wypluwania czy naprawiania kodu. Chociaż dobry jest do nauki, ale trzeba go umieć prowadzić i zadawać pomocnicze pytania. To bardziej jakbyś miał rozmowę z kimś o większej wiedzy niż twoja, ale kimś, kto niekoniecznie zrozumie twoje intencje czy zna cały kontekst.

Trochę tak jakbyś gadał z doświadczonym programistą, który by ci tłumaczył i pisał kod długopisem na kartce. Możesz się nauczyć, zainspirować, ale nie tak, że da ci pełne rozwiązanie.

No właśnie moim zdaniem czasem nawet to nie pomaga, bo chat gpt jak nie zna jakiejś odpowiedzi to nie powie "nie wiem", tylko wypluje jakieś głupoty czy po prostu nieprawdziwe informacje. ChatGPT robi czasem mega głupie błędy, takich których żaden amator by nie popełnił. I możesz je wyłapać tylko jak znasz temat - jak się uczysz to ich nie wyłapiesz.

1 użytkowników online, w tym zalogowanych: 0, gości: 1