loading...

re: Challenge - Print Spiral VIEW POST

FULL DISCUSSION
 

Got it to Level 2, without using arrays at all.
On level 2 this becomes purely a math problem, so its solutions will be probably pretty similar in any language (except formulas used can be more efficient), but I'm sticking to Rust as I'm learning it right now.

The idea of my solution is that the spiral can be split into series of numbers (called layers here) which run starting from n^2 to n^2 + n, then spiral turns left and runs n numbers more. That 'n' depends on a distance from the spiral center, and starting position will be either top left or bottom right corner, but that's all the difference between them.

#![feature(inclusive_range_syntax)]

const SPIRAL_SIDE: u32 = 10;

fn main() {
    let border = (SPIRAL_SIDE / 2) as i32;
    let (min, max) = if SPIRAL_SIDE % 2 == 0 {
        (-border + 1, border)
    } else {
        (-border, border)
    };

    for y in (min..=max).rev() {
        (min..=max)
            .into_iter()
            .for_each(|x| print!("{:>3} ", number_at(x, y)));
        println!();
    }
}

fn number_at(x: i32, y: i32) -> i32 {
    let distance_from_center = x.abs().max(y.abs());

    if y > -x {
        calculate_number_for(x, y, 2 * distance_from_center - 1)
    } else {
        calculate_number_for(-x, -y, 2 * distance_from_center)
    }
}

fn calculate_number_for(x: i32, y: i32, layer_id: i32) -> i32 {
    let bottom_right_num = layer_id.pow(2);
    let top_right_num = bottom_right_num + layer_id;

    let dist_from_top_right = (x - y).abs();

    if y <= x {
        top_right_num - dist_from_top_right
    } else {
        top_right_num + dist_from_top_right
    }
}

You can run this script here play.rust-lang.org/?gist=1548e5d81..., spiral size can be changed by adjusting the const SPIRAL_SIDE at the top.

 

In my first reflections of the code, I picked numbers in the mirrored case where 'bottom_right_num' was top left and 'top_right_num' was bottom left. Picking one with y > -x (above the secondary diagonal) it was easier to understand. Then I could transfer it to the mirrored case. Different names in different cases perhaps could be easier to understand, on the other hand I had kind of bad luck with my choice of examples. ;)

Thanks for your solution and for the link to the playground.

 

Well, when I finally got the pattern with numbers, it was at the top-right side, so the code kind of reflects this 🙂

 

Seems like Rust functions return the final statement (for each case) without a return keyword? Edit: Ah, I see, implicit returns.

 

Yes, final statement is returned. Actually, almost anything with a block in Rust yields a final block statement as a value, so it's possible, for example, write something like let x = if y < 0 { -y } else { y };

That's quite common in functional programming, btw. When I read about Rust in early 2016, the most impressive and likeable idea was borrowing. And, of course, pattern matching. I like to see some Rust here.

 

If you want to use it in the stable channel, just change the main() to

fn main() {
    let border = (SPIRAL_SIDE / 2) as i32;
    let (min, max) = if SPIRAL_SIDE % 2 == 0 {
        (-border + 1, border + 1)
    } else {
        (-border, border)
    };

    for y in (min..max).rev() {
        (min..max)
            .into_iter()
            .for_each(|x| print!("{:>3} ", number_at(x, y)));
        println!();
    }
}

And no need of

#![feature(inclusive_range_syntax)]
Code of Conduct Report abuse