Code golf with a HashSet intersection algorithm

Hi All, I have some working code but would be interested in suggestions on how to make it more idiomatic and/or cleaner. The algorithm is that I build the union of sets and then take the intersection of that large set with another, smaller set. It feels like I should be able to use the Union and Intersection structures more cleanly rather than my current, verbose "iterate and insert into a temporary set" code.

    // Build a set of all the tiles that are illuminated in the map
    let mut illuminated_tiles = HashSet::new();
    let mut query_light = <(&Point, &FieldOfLight)>::query();
    query_light.iter(ecs)
        .for_each(|(pt, fol)| {
            let lit_tiles = field_of_view_set(*pt, fol.radius, map);
            // illuminated_tiles = illuminated_tiles.union(&lit_tiles).collect::<HashSet<Point>>();
            for pt in lit_tiles.into_iter() {
                illuminated_tiles.insert(pt);
            }
        });

    // The viewable tiles is the intersection between the actors
    // view radius and the set of illuminated tiles
    let mut query_view = <(&Point, &mut FieldOfView)>::query();
    query_view.iter_mut(ecs)
        .filter(|(_, fov)| fov.is_dirty )
        .for_each(|(pt, fov)| {
            let pt = *pt;
            let view_set = field_of_view_set(pt, fov.radius, map);
            let intersection = illuminated_tiles.intersection(&view_set);
            
            let mut view_set = HashSet::new();
            for pt in intersection {
                view_set.insert(*pt);
            }
            fov.visible_tiles = view_set;
            fov.is_dirty = false;
        });

illuminated_tiles.extend(lit_tiles)

1 Like

something like this ought to work:

    // Build a set of all the tiles that are illuminated in the map
    let mut query_light = <(&Point, &FieldOfLight)>::query();
    let illuminated_tiles = query_light
        .iter(ecs)
        .flat_map(|(pt, fol)| field_of_view_set(*pt, fol.radius, map))
        .collect::<HashSet<_>>();
1 Like

perhaps:
fov.visible_tiles = &illuminated_tiles & &field_of_view_set(*pt, fov.radius, map);

1 Like

This worked perfectly and seems very clean. The other solution (using extend) also worked. If you were doing a code review would you prefer one over the other?

EDIT: wish I could mark more than one response as the Solution.

That worked too. I feel a little dumb missing the & operator on HashSet. I assumed the default behavior of intersection would do what this does. Thank you.

EDIT: after refactors the code looks like the following. The & looks a little magical to me (something I'd expect to see in ruby) but I'll get used to it.

    // Build a set of all the tiles that are illuminated in the map
    let mut query_light = <(&Point, &FieldOfLight)>::query();
    let illuminated_tiles = query_light.iter(ecs)
        .flat_map(|(pt, fol)| field_of_view_set(*pt, fol.radius, map))
        .collect::<HashSet<_>>();

    // The viewable tiles is the intersection between the actors
    // view radius and the set of illuminated tiles
    let mut query_view = <(&Point, &mut FieldOfView)>::query();
    query_view.iter_mut(ecs)
        .filter(|(_, fov)| fov.is_dirty )
        .for_each(|(pt, fov)| {
            fov.visible_tiles = &illuminated_tiles & &field_of_view_set(*pt, fov.radius, map);
            fov.is_dirty = false;
        });

Iā€™d prefer the one using flat_map.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.