DEV Community

Discussion on: Challenge - Print Spiral

Collapse
 
yondrin profile image
Alex Doroshenko • Edited

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.

Collapse
 
heikodudzus profile image
Heiko Dudzus • Edited

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

Collapse
 
yondrin profile image
Alex Doroshenko

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 };

Thread Thread
 
heikodudzus profile image
Heiko Dudzus

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.

Collapse
 
nachoba profile image
Ignacio Sniechowski • Edited

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)]
Collapse
 
heikodudzus profile image
Heiko Dudzus

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.

Collapse
 
yondrin profile image
Alex Doroshenko • Edited

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