Rotate a camera; save an Astronaut

23

4

Note: Minor spoilers for The Martian are in this challenge. Read ahead with caution


The Martian is a Science Fiction novel about astronaut and botanist extraordinaire, Mark Watney, who has been accidentally stranded on Mars. At one point in the book, Mark tries communicating with NASA, but the only means of communication they have is a camera. Mark sends messages by writing on index cards, and, since NASA can rotate the camera 360 degrees, NASA sends replies back by pointing the camera at cards labeled "Yes" or "No".

Since the only data NASA can send is the direction the camera is facing, Mark comes up with a system where they can point at cards with alphabet characters on them to type messages. But using the letters 'a-z' would be impractical. To quote the book (from this answer, over on scifi.se):

We’ll need to talk faster than yes/no questions every half hour. The camera can rotate 360 degrees, and I have plenty of antenna parts. Time to make an alphabet. But I can’t just use the letters A through Z. Twenty-six letters plus my question card would be twenty-seven cards around the lander. Each one would only get 13 degrees of arc. Even if JPL points the camera perfectly, there’s a good chance I won’t know which letter they meant.

So I’ll have to use ASCII. That’s how computers manage characters. Each character has a numerical code between 0 and 255. Values between 0 and 255 can be expressed as 2 hexadecimal digits. By giving me pairs of hex digits, they can send any character they like, including numbers, punctuation, etc.

...

So I’ll make cards for 0 through 9, and A through F. That makes 16 cards to place around the camera, plus the question card. Seventeen cards means over 21 degrees each. Much easier to deal with.

Your goal today, as one of the top software engineers at NASA, is to write a program to encode the various angles of the camera. The seventeen cards Mark has for you to point at are (in order):

?0123456789ABCDEF

and each of these cards is 21 degrees apart, so to rotate the camera from ? to 0, you should rotate the camera 21 degrees, and 2 to 1 is -21 degrees. (It's not exactly 21, but we'll round to keep it simpler) This wraps, so to go from F to 3 is 105 degrees (5 turns, 5 * 21 = 105). This is more efficient than going -252, since the camera won't have to move as far.

Here's what your program or function must do.

  1. Take a string as input. We'll call this string s. To keep it simple, we'll say that the input will only ever be printable ASCII. For our example, let's say that the input was STATUS

  2. Convert each character to its hexadecimal representation. This would convert STATUS to 53 54 41 54 55 53.

  3. Print out or return the consecutive degree turns the camera will need to make in order to point at each card and return to the "Question Card". For our example, this would be:

    6  * 21 = 126   (?-5)
    -2 * 21 = -42   (5-3)
    2  * 21 = 42    (3-5)
    -1 * 21 = -21   (5-4)
    0  * 21 = 0     (4-4)
    -3 * 21 = -63   (4-1)
    4  * 21 = 84    (1-5)
    -1 * 21 = -21   (5-4)
    1  * 21 = 21    (4-4)
    0  * 21 = 0     (5-5)
    0  * 21 = 0     (5-5)
    -2 * 21 = -42   (5-3)
    -4 * 21 = -84   (3-?)
    

    Or, in array format:

    [126, -42, 42, -21, 0, -63, 84, -21, 21, 0, 0, -42, -84]
    

Note that you must always take the smallest of the possible rotations. So if the input was NO, which is 4E 4F, you should output:

5    * 21 = 105
-7   * 21 = -147
7    * 21 = 147
-6   * 21 = -126
1    * 21 = 21

Rather than:

 5   * 21 = 105
 10  * 21 = 210
 -10 * 21 = -210
 11  * 21 = 231
 -16 * 21 = -336

Here are some more worked examples:

Input: CROPS?
ASCII: 43 52 4F 50 53 3F
Worked Example:

5  * 21 = 105
-1 * 21 = -21
2  * 21 = 42
-3 * 21 = -63
2  * 21 = 42
-6 * 21 = -126
7  * 21 = 147
-5 * 21 = -105
5  * 21 = 105
-2 * 21 = -42
0  * 21 = 0
-5  * 21 = -105
1 * 21 = 21

Result: [105 -21 42 -63 42 -126 147 -105 105 -42 0 -105 21]


Input: DDD
ASCII: 44 44 44
Worked Example:

5   * 21 = 105
0   * 21 = 0
0   * 21 = 0
0   * 21 = 0
0   * 21 = 0
0   * 21 = 0
-5  * 21 = -105

Result: [105, 0, 0, 0, 0, 0, -105]


Input: Hello world!
ASCII: 48 65 6c 6c 6f 20 77 6f 72 6c 64 21
Worked example:

5   * 21 = 105
4   * 21 = 84
-2  * 21 = -42
-1  * 21 = -21
1   * 21 = 21
6   * 21 = 126
-6  * 21 = -126
6   * 21 = 126
-6  * 21 = -126
-8  * 21 = -168
4   * 21 = 84
-2  * 21 = -42
7   * 21 = 147
0   * 21 = 0
-1  * 21 = -21
-8  * 21 = -168
-8  * 21 = -168
-5  * 21 = -105
4   * 21 = 84
6   * 21 = 126
-6  * 21 = -126
-2  * 21 = -42
-2  * 21 = -42
-1  * 21 = -21
-2  * 21 = -42

Result: [105 84 -42 -21 21 126 -126 126 -126 -168 84 -42 147 0 -21 -168 -168 -105 84 126 -126 -42 -42 -21 -42]

Since NASA prides itself on efficiency, your goal is to write the shortest code possible. Standard loopholes apply. Now bring him home!

James

Posted 2017-03-01T20:42:49.933

Reputation: 54 537

Side note: These test cases were made by hand, and were kind of a pain, so there might be some minor inaccuracies. Please let me know if anything looks wrong. :) – James – 2017-03-01T20:50:26.260

Related – Digital Trauma – 2017-03-01T22:19:13.787

Answers

5

JavaScript (ES6), 103 99 bytes

s=>[...s.replace(/./g,c=>c.charCodeAt().toString(16)),10].map(n=>((24-p-~(p='0x'+n))%17-8)*21,p=-1)

Test cases

let f =
s=>[...s.replace(/./g,c=>c.charCodeAt().toString(16)),10].map(n=>((24-p-~(p='0x'+n))%17-8)*21,p=-1)

let g = s => console.log(`f('${s}'): [${f(s)}]`)
g('STATUS')
g('NO')
g('CROPS?')
g('DDD')
g('Hello world!')

ETHproductions

Posted 2017-03-01T20:42:49.933

Reputation: 47 880

Would this work? s.replace(/./g, -> [...s].map( – Luke – 2017-03-02T09:51:08.710

@Luke Nope, because we need to separate out each hexadecimal digit. ...s.replace(/./g, gives e.g. "4","8","6","5","6","c"..., while. ...[...s.map( would give "48","65","6c",... – ETHproductions – 2017-03-02T14:05:20.300

4

C, 212 202 199 187 bytes

3 bytes saved thanks to @KritixiLithos!

i;f(a,b){i=abs(a-b);i=8>i?i:17-i;i=a<b&a>b-8?i:a<b&a<b-8?-i:b<a&b>a-8?-i:i;i*=21;}v;g(char*s){for(v=0;*s;s+=v++%2)printf("%d ",v?v%2?f(*s%16,s[1]?s[1]/16:-1):f(*s/16,*s%16):f(-1,*s/16));}

Try it online!

betseg

Posted 2017-03-01T20:42:49.933

Reputation: 8 493

1I think you can do 8>i?i:17-i instead of 17-i>i?... – user41805 – 2017-03-02T08:33:00.423

@KritixiLithos yup, thanks. – betseg – 2017-03-02T08:36:05.897

3

Python, 187 178 bytes

def g(x):w,z=map('?0123456789abcdef'.index,x);d=w-z;return min(d,d+17*(d<=0 or -1),key=abs)*21
def f(s):s=''.join(map('{:2x}'.format,s.encode()));return[*map(g,zip(s+'?','?'+s))]

Test cases

for k in ['STATUS', 'NO', 'CROPS?', 'DDD', 'Hello world!']:
    print('Input:  {}\nOutput: {}'.format(k, f(k)))

Uriel

Posted 2017-03-01T20:42:49.933

Reputation: 11 708

2

Python 2, 135 112 bytes

h=reduce(lambda l,r:l+(r/16,r%16),map(ord,input()),())
print[(((r-l+8)%17)-8)*21for l,r in zip((-1,)+h,h+(-1,))]

Try it online!

Jonathan Allan

Posted 2017-03-01T20:42:49.933

Reputation: 67 804

1

Jelly, 21 19 bytes

Ob⁴F-;;-I+8%17_8×21

Try it online!

How?

Ob⁴F-;;-I+8%17_8×21 - Main link: string s        e.g. 'e.g.'
O                   - cast to ordinals            [101,     46,       103,      46]
 b                  - convert to base
  ⁴                 -     16                   [[6,   5], [2,  14], [6,   7], [2,  14]]
   F                - flatten                   [6,   5,   2,  14,   6,   7,   2,  14]
    -;              - -1 concatenate      [-1,   6,   5,   2,  14,   6,   7,   2,  14]
      ;-            - concatenate -1      [-1,   6,   5,   2,  14,   6,   7,   2,  14,  -1]
        I           - increments            [  7,  -1,  -3,  12,  -8,   1,  -5,  12, -15]
         +8         - add 8                 [ 15,   7,   5,  20,   0,   9,   3,  20,  -7]
           %17      - mod 17                [ 15,   7,   5,   3,   0,   9,   3,   3,  10]
              _8    - subtract 8            [  7,  -1,  -3,  -5,  -8,   1,  -5,  -5,   2]
                ×21 - multiply by 21        [147, -21, -63,-105,-168,  21,-105,-105,  42]

Jonathan Allan

Posted 2017-03-01T20:42:49.933

Reputation: 67 804

1

Ohm, 20 19 bytes (CP437), non-competing

EDIT: Saved 1 byte by changing a map block to repeated single-component maps.

Would probably be quite a bit shorter if I had implicit vectorization.

`»x»}{»úΓXΓHδ▓_~21*

Explanation:

`»x»}{»úΓXΓHδ▓_~21*    Main wire, arguments: s

`»x                    Convert char codes of s to hex
   »}                  Split digit pairs
     {                 Flatten
      »ú               Convert digits back to base 10
        ΓXΓH           Append and prepend with -1
            δ          Get deltas between each element of array
             ▓         Map array over...
              _~21*      Negate, multiply by 21

Nick Clifford

Posted 2017-03-01T20:42:49.933

Reputation: 1 184

0

PHP, 125 116 bytes:

function m($i){static$a;$a+=$d=($i-$a+10)%17-9;echo$d*21,"
";}for(;$c=ord($argv[1][$i++]);m($c%16))m($c/16|0);m(-1);

breakdown

function m($i)              // function to turn camera:
{
    static$a;                   // remember angle
    $a+=                        // add delta to angle
    $d=($i-$a+10)%17-9;         // delta: target=nibble value+1-current angle
                                // add 9, modulo 17, -9 -> shortest movement
    echo$d*21,"\n";                 // print delta * 21 and a linebreak
}
for(;$c=ord($argv[1][$i++]);// loop through input characters
    m($c%16))                   // 2. move to low nibble value
    m($c/16|0)                  // 1. move to high nibble value
;
m(-1);                      // move back to "?"

Of course, 21 is pretty inaccurate and may fail for strings longer than 14 characters; but then ... 360/17 would be four bytes longer.

An alternative solution would have been to attach a laser pointer to the camera;
we could use all printable ascii chars and a "question" card at 3.75 degrees each with that.

Another alternative: Use 16 cards (at 22.5 degrees) with 6 characters each:
implement some sort of T9 and we can omit the high nibble. ;)

Titus

Posted 2017-03-01T20:42:49.933

Reputation: 13 814