Friday, March 18, 2011

Square?

A recent code golf requested a way to determine if 4 points form a square. Factor may not be the shortest answer, but I thought I would contribute it anyway.

We require four unique points to be provided. We then use the distance formula to compute the distances between all pairs of points. For a square, there should be just two lengths (the side and the diagonal) not counting zero (the distance from a point to itself).

USING: kernel math math.combinatorics math.vectors sequences sets ;

: square? ( seq -- ? )
    members [ length 4 = ] [
        2 [ first2 v- [ sq ] map-sum ] map-combinations
        { 0 } diff length 2 =
    ] bi and ;

We can write some unit tests to make sure it works.

USE: tools.test

[ t ] [
    {
        { { 0 0 } { 0 1 } { 1 1 } { 1 0 } }   ! standard square
        { { 0 0 } { 2 1 } { 3 -1 } { 1 -2 } } ! non-axis-aligned square
        { { 0 0 } { 1 1 } { 0 1 } { 1 0 } }   ! different order
        { { 0 0 } { 0 4 } { 2 2 } { -2 2 } }  ! rotated square
    } [ square? ] all?
] unit-test

[ f ] [
    {
        { { 0 0 } { 0 2 } { 3 2 } { 3 0 } }   ! rectangle
        { { 0 0 } { 3 4 } { 8 4 } { 5 0 } }   ! rhombus
        { { 0 0 } { 0 0 } { 1 1 } { 0 0 } }   ! only 2 distinct points
        { { 0 0 } { 0 0 } { 1 0 } { 0 1 } }   ! only 3 distinct points
    } [ square? ] any?
] unit-test

Since it's code golf (fewest characters possible), how might you make it shorter?

3 comments:

Alfredo Beaumont said...

It's not possible to combine a point with itself, and all of them are different (due to initial members), therefore you can replace { 0 } diff by members... not so big gain, but still :)

Apart from that... just use 1 character for word name and stack effect declaration elements ": s ( s -- ? )" :)

Alfredo Beaumont said...

And of course removing cosmetic spaces and USING: vocab, since only function definition is requested, even if this would ofuscate de code

Jon said...

"v- [ sq ] map-sum" is distance (from math.vectors)