Measuring Rectangles

Challenge

Your goal is to write a program which will output the 8 measurements of a rectangle, with only some measurements in the input. Your program should throw an error if it cant be done.

Here is what each measurement means:

|          left
|          |      centerX
|          |      |      right
|          |      |      |
|          v      v      v
|           _____________
|       ^  |             |  <---top
|       |  |             |
|       |  |             |
|height |  |             |  <---centerY
|       |  |             |
|       |  |             |
|       v  |_____________|  <---bottom
|
|          <----width---->
|________________________________________


Input/Output Formats:

Input is in the form of some measurements of a box, one on each line. Output should be all 8 measurements of the corresponding box (or an error if the given measurements can not be used to find the other measurements).

For example, an input of

left 6
centerX 10
top 40
height 20


should print out

left 6
right 14
top 40
bottom 20
centerX 10
centerY 30
width 8
height 20


This is code-golf, so the shortest code wins.

• It's unclear what the input should be. Oct 3 '15 at 3:56
• I don't get it. Generally, you will need 4 values (two of the horizontal, two of the vertical) to define the rectangle. Is the goal to get 4 values as input, and produce all of the values in the list? Just guessing, because it seems quite unclear. Oct 3 '15 at 3:58
• The first sentence is a bit unclear as well—does only two pairs mean 4? This seems to conflict with the second sentence, given any pair of coordinates.
– Doorknob
Oct 3 '15 at 3:58
• i added some edits, sorry for the phrasing! Oct 3 '15 at 4:06
• Is my attempt as a function valid? MFC: pastebin.com/raw/Pbi68pLj Jul 7 '17 at 14:09

Python 2 with SWI Prolog ('typical' install), on Windows, 595 565 bytes.

import sys,subprocess as w;o='left right top bottom centerX centerY width height'
g={k:v for k,v in [l.strip().split() for l in sys.stdin.readlines()]};s='L,R,T,B,C,D,W,H';
open('t','w').write(
":-use_module(library(clpfd)).\nr(%s):-L#=R-2*(R-C),L#=R-W,L#=C-W//2,B#=T-2*(T-D),B#=T-H,B#=D-H//2,%s."%
(s,','.join('writeln(%s)'%x for x in s.split(','))));r='\n'.join([a+' '+b for (
a,b) in zip(o.split(),w.check_output("swipl -q -f t -t r(%s)."%(','.join(g.get(
x,x[0].replace('centerY','d').upper()) for x in o.split()))).split('\r\n'))])
print '!' if '_' in r else r


e.g. with a text file for input, containing:

right 14
centerX 10
top 40
height 20


it does this:

D:\>type rectin.txt|c:\python27\python.exe d:\rect.py
left 6
right 14
top 40
bottom 20
centerX 10
centerY 30
width 8
height 20


And in the error condition if the contents of the stdin file are not enough to calculate the other values, it does this:

D:\>type rectin.txt|c:\python27\python.exe d:\rect.py
!


NB. It relies on swipl.exe being in the PATH

Explanation

I started writing in Python, got bored of expressing all the possible combinations of relationship (each one can be derived from three different combinations). I thought "must be a job for Prolog". So I installed a Prolog, headed for a tutorial, and got something that just about works. It's this:

:-use_module(library(clpfd)).

r(L,R,T,B,C,D,W,H):-
L#=R-2*(R-C),
L#=R-W,
L#=C-W//2,
B#=T-2*(T-D),
B#=T-H,
B#=D-H//2,

writeln(L),
writeln(R),
writeln(T),
writeln(B),
writeln(C),
writeln(D),
writeln(W),
writeln(H).


It uses the clpfd library (part of the 'typical install' requirement), which provides 'declarative integer arithmetic'. This gives the big advantage that I only need to define Left in terms of (Right and CenterX), (Right and Width), (CenterX and Width). From that, it can derive Right, Center and Width. (Same with the vertical set Top/Bottom/CenterY/Height).

This also means I don't have to check which specific values are provided; I give all the available values and placeholders for the rest, and it either calculates the answers, or outputs some unknown variable placeholders.

Being extremely novice at Prolog, handling STDIN was too much for me right now, so I wrapped it with Python. I don't have an ungolfed version of the Python, but it's not doing anything too amazing; it reads STDIN into a dictionary of {measurement:value} pairs, then puts them in the preset order the Prolog code is expecting, merged with the uppercase first letter of the unknowns.

(There's an annoyance where all the measurements are unique by first letter, except centerX and centerY, so it replaces centerY with D).

Then it writes the Prolog script out to a file, shells out to Prolog to run it and read the result, parses the number output and pairs them up with the measurements in order.

Then checks if any of the outputs were uncomputable and throws an error (!), otherwise prints the result.

• Awesome use of Prolog and CLP(FD) constraints! You can combine all these writeln/1 calls into a single call of format/2. For example, try: ?- format("~w~n~w~n~w~n", [a,b,c]). to see what I mean. To read from standard input, I recommend you use a DCG and then apply the DCG to user_input with SWI-Prolog's library(pio) and phrase_from_stream/2. See library(dcg/basics) for some useful definitions. I hope that this will allow you to shorten your code significantly.
– mat
Oct 7 '15 at 15:22
• @mat Thanks for the suggestions! (But they look too intimidating. I think I lucked out with how easy CLP(FD) is to use, after that I couldn't even make a recursive list-is-sorted-if predicate. I'm nowhere near applying a DCG to an io stream). (All my writeln/1 calls are generated from a Python list comprehension, they're already shorter than a basic format("~w~n~w~n~... call would be, even if I had known it existed). Oct 10 '15 at 5:37

MFC, 212

void P(int l,int t,int r,int b){CRect R(l,t,r,b);POINT c=R.CenterPoint();TRACE("Left:%d Top:%d Right:%d Bottom:%d CenterX:%d CenterY:%d Width:%d H:%d",R.left,R.top,R.right,R.bottom,c.x,c.y,R.Width(),R.Height());}


MFC, 213

void P(int l,int t,int r,int b){CRect R(l,t,r,b);CPoint c=R.CenterPoint();TRACE("Left:%d Top:%d Right:%d Bottom:%d CenterX:%d CenterY:%d Width:%d H:%d",R.left,R.top,R.right,R.bottom,c.x,c.y,R.Width(),R.Height());}


Outputs the result to Visual Studio's Debug Output Window.

Example:

P(12,7,23,30);


outputs

Left:12 Top:7 Right:23 Bottom:30 CenterX:17 CenterY:18 Width:11


Uncondensed format:

void P(int l, int t, int r, int b)
{
CRect R(l, t, r, b);
CPoint c= R.CenterPoint();
TRACE("Left %d Top:%d Right:%d Bottom:%d CenterX:%d CenterY:%d Width:%d H:%d", R.left, R.top, R.right, R.bottom, c.x, c.y, R.Width(), R.Height());
}