Code (Mini) Golf

50

12

Given a side-view of a mini-golf course and the power of the swing, determine if the ball will make it into the hole.


A course will be in this format:

      ____       ____ _   
   __/    \     /    U \  
__/        \   /        \_
            \_/           

The ball starts directly before the first piece of ground on the left and follows the contour of the course until it reaches the hole (an upper-case U below the current level of the ground). If it reaches the hole, output a truthy value. The power of the swing will be the initial speed of the ball. The ball moves to the next character on the right at each iteration, then the speed is altered depending on the character it is now on. If the speed reaches 0 or less before the hole, output a falsey value.

  • _ decreases the speed by 1
  • / decreases the speed by 5
  • \ increases the speed by 4

Courses can optionally be padded with spaces. The power of the swing will always be a positive integer.

You do not need to worry about the ball going too fast to enter the hole, rolling backwards or jumping/bouncing off hills.

Test Cases

Input: 27
      ____       ____ _   
   __/    \     /    U \  
__/        \   /        \_
            \_/           
Output: true

----------

Input: 26
      ____       ____ _   
   __/    \     /    U \  
__/        \   /        \_
            \_/           
Output: false

----------

Input: 1

U
Output: true

----------

Input: 1
_ 
 U
Output: false

----------

Input: 22

     /U
    /  
   /   
  /    
\/     
Output: true

----------

Input: 999
_       _
 \     / 
  \   /  
   \ /   
    U    
Output: true

----------

Input: 5
  /
/U 
Output: false

----------

Input: 9

/\/\/\/\/U
Output: false

----------

Input: 16

_/\                                         _
   \      __       /\/\/\                  / 
    \    /  \     /      \                /  
     \__/    \   /        \____________ _/   
              \_/                      U     

Output: true

This is code mini-golf, shortest answer in bytes wins!

user81655

Posted 2016-01-19T23:40:07.100

Reputation: 10 181

1If your language has good array built-ins, then you can turn the input into a stream of operations (\_/) with the following steps: split into array of lines, rotate, flatten, strip spaces. – Cyoce – 2016-01-20T00:09:24.327

1This is really more of a fixed-track mechanism than a golf course :P – Zach Gates – 2016-01-20T01:27:19.240

24I like that \/\/\/\/\/ is a more efficient course than __________. – ezrast – 2016-01-21T00:53:31.503

2That's what I was thinking, 4 down, 5 up, then .5 must be average. Oh, flat is 1? – Leif Willerts – 2016-01-21T13:21:29.647

Will each line in a course always be the same length (with trailing spaces filling in the end of shorter lines)? – SnoringFrog – 2016-01-25T17:02:18.083

@SnoringFrog If you want it to. That's what it means by "Courses can optionally be padded with spaces". – user81655 – 2016-01-25T21:31:11.210

Cool. Wasn't sure if that meant "you can if you want" or "they may or may not come like that so code for both" – SnoringFrog – 2016-01-25T21:35:23.553

Answers

17

Pyth, 27 bytes

.Am<sXsd"_\ /"[1_4Z5)Q._C.z

Demonstration

This code does something very clever and not at all type-safe with X. Check it out below.

Explanation:

.Am<sXsd"_\ /"[1_4Z5)Q._C.z
                               Implicit: Z = 0, Q = eval(input())
                               Q is the initial power.
                         .z    Take all input, as a list of lines.
                        C      Transpose, giving all columns.
                      ._       Form all prefixes.
  m                            Map over the prefixes.
      sd                       Concatenate the prefix.
     X  "_\ /"[1_4Z5)          Change '_' to 1, '\' to -4, ' ' to 0, and '/' to 5.
                               In particular, 'U' is left unchanged.
    s                          Reduce on addition.
                               If all elements were numbers,
                               this results in the total change in power.
                               If there was a 'U', it results in a string.
   <                 Q         If the previous result was a number, this compares
                               it with the initial input to see if the ball is
                               still rolling.
                               If the previous result was a string, this slices off
                               the first Q characters, which always has a truthy
                               result.
.A                             Test whether all of the prefixes mapped to a thruthy
                               result.

isaacg

Posted 2016-01-19T23:40:07.100

Reputation: 39 268

I might be missing something, but does it stop at Q? Ie the last example could cause some issues? – flindeberg – 2016-01-20T14:01:03.613

@flindeberg That's not how it works. The < ... Q works as a numeric comparison up until the hole, not a slice. After the hole, all that matters is that the result is truthy. – isaacg – 2016-01-20T22:45:35.207

14

Haskell, 111 109 bytes

import Data.List
g"_"=1
g"/"=5
g _= -4 
f n=all(>0).scanl(-)n.map g.fst.span(/="U").(>>=words).transpose.lines

Usage example:

*Main> f 27 "      ____       ____ _   \n   __/    \\     /    U \\  \n__/        \\   /        \\_\n            \\_/           "
True
*Main> f 26 "      ____       ____ _   \n   __/    \\     /    U \\  \n__/        \\   /        \\_\n            \\_/           "
False

How it works:

                            lines  -- split into list of lines at nl
                       transpose   -- transpose
                  (>>=words)       -- turn each line into words (i.e. remove spaces)  
            fst.span(/="U")        -- take all words up to but excluding "U"
         map g                     -- turn each word into the speed modifier
    scanl(-)n                      -- build list of partial sums starting with n
                                   --   note: speed modifiers are negative so we
                                   --   use (-) with scanl to build sums 
all(>0)                            -- return true if all sums are greater than 0                                 

Edit: @user81655 found 2 bytes to save. Thanks!

nimi

Posted 2016-01-19T23:40:07.100

Reputation: 34 639

7

Ruby, 104 87 characters

->s,t{t.lines.map(&:bytes).transpose.map{|o|(c=o.max)==85||s<0?break: s+=c*3%14-6}
s>0}

Sample run:

2.1.5 :001 > track = '      ____       ____ _   
2.1.5 :002'>    __/    \     /    U \  
2.1.5 :003'> __/        \   /        \_
2.1.5 :004'>             \_/           
2.1.5 :005'> '
 => "      ____       ____ _   \n   __/    \\     /    U \\  \n__/        \\   /        \\_\n            \\_/           \n" 

2.1.5 :006 > ->s,t{t.lines.map(&:bytes).transpose.map{|o|(c=o.max)==85||s<0?break: s+=c*3%14-6};s>0}[27, track]
 => true 

2.1.5 :007 > ->s,t{t.lines.map(&:bytes).transpose.map{|o|(c=o.max)==85||s<0?break: s+=c*3%14-6};s>0}[26, track]
 => false 

manatwork

Posted 2016-01-19T23:40:07.100

Reputation: 17 865

6

Japt, 38 bytes

Vz r"%s|U[^]*" ¬e@UµX¥'_?1:X¥'/?5:-4 ¬

Try it here!

Beating CJam!

Explanation

Basically takes the string input, rotates it 90deg clockwise, strips out spaces and newlines, removes the hole and everything after it, and splits along chars. Then checks if ball ever gets to zero or below using the every function.

Mama Fun Roll

Posted 2016-01-19T23:40:07.100

Reputation: 7 234

I think \ should be positive (description looks wrong) – isaacg – 2016-01-20T05:57:24.203

I don't think that works. Picture this: a series of slopes gets the ball's speed to -2, but then there are a net +4 later on. The sum would reflect +2 so the ball made it. In reality, it would never get to the positive section after reaching the negatives. – Cyoce – 2016-01-20T06:13:52.133

I think I fixed the problem. – Mama Fun Roll – 2016-01-20T06:40:02.780

That's a cool button ;) – J Atkin – 2016-01-20T14:32:57.240

Nice! So, golfing... The double backslash is replaceable with %, and >0 can be replaced with ¬, since the sqrt of a non-positive number is always falsy (0 -> 0, -1 -> NaN). – ETHproductions – 2016-01-20T21:54:48.587

Lol why didn't I think of that? – Mama Fun Roll – 2016-01-20T23:15:22.987

BTW, how is [^] different than .? – ETHproductions – 2016-01-21T15:41:21.673

[^] also matches line breaks. – Mama Fun Roll – 2016-01-21T23:48:10.097

6

CJam, 40 39 bytes

liqN/:.e>'U/0="\_/"[4W-5]er{1$+}/]:e<0>

Input has the power on the first line and the course starting on the second line. Output is 0 or 1.

Test it here.

Explanation

li    e# Read power and convert to integer.
qN/   e# Read course and split into lines.
:.e>  e# Flatten course by folding maximum over columns.
'U/   e# Split around the hole.
0=    e# Keep the first chunk.
"\_/"[4W-5]er
      e# Replace \, _, / with 4, -1, 5, respectively.
{     e# For each of those costs...
  1$+ e#   Copy the previous power and add the cost.
}/    e# This leaves all partial sums on the stack.
]     e# Wrap them in an array.
:e<   e# Find the minimum.
0>    e# Check whether it's positive.

Martin Ender

Posted 2016-01-19T23:40:07.100

Reputation: 184 808

5

Retina, 82 81 77 74 68 67 68 bytes

+`(?<=(.)*) (?=.*¶(?<-1>.)*(.))
$2
\\
>>>>
+`>_|>{5}/|>¶

^>*U

Try it online

  • Input is represented in unary base, as n >s - for example, 4 is >>>>\n. (is this legal?)
  • +`(?<=(.)*) (?=.*¶(?<-1>.)*(.)) $2 - flatten the course - replace spaces with the character below them.

    After this stage the data will look like this:

    >>>>>>>>>>>>>>>>>>>>>>>>>>
    __/__/____\\\_///____U_\\_
    __/__/    \\\_///    U \\_
    __/        \\_//        \_
                \_/           
    

    We can just ignore everything after the first U, we will not reach there anyway.

  • > represent a step we are allowed to make, or the remaining energy.
  • Replace each \ with four > - a slope gives us additional energy.
  • Loop: contentiously remove >_ or >>>>>/ until there are none left. _s and /s consume energy.
  • Finally, try to match ^>*U - check if we can reach U with positive energy (or no energy).
    This will output 0 or 1.

Another close option with 91 79 bytes is:

+`(?<=¶(.)*) (?=.*¶(?<-1>.)*(.))
$2
^(>)+\n(?<-1>_|/(?<-1>){4}|\\(?<1>){5})+U

Try it online

This is the same approach but with a balancing group instead of a contentious replace.

I'm sure both of these can be golfed further, so any one of them may end up shorter.

Kobi

Posted 2016-01-19T23:40:07.100

Reputation: 728

1

Yes, unary input is legit (unless the challenge specifies "decimal"), although I'd probably use 0 or 1 as the digit if that doesn't incur any additional bytes.

– Martin Ender – 2016-01-21T14:27:31.107

1Also welcome to PPCG, I'm really glad to see you here! :) (And using Retina as well.) – Martin Ender – 2016-01-21T14:28:34.663

Sure! It was on the hot question list and looked fun. I thought I'd give it a try :-) – Kobi – 2016-01-21T14:29:22.023

3

Python (3.5) 169 160 bytes

A recursive solution without the transpose function (zip)

def f(c,p):c=c.splitlines();l=len(c);f=lambda x,h,v:v if'U'==c[h][x]or v<1 else f(x+(h==l-1),(h+1)%l,v+{"_":-1,"\\":4,"/":-5," ":0}[c[h][x]]);return f(0,0,p)>0

Ungolfed

c for course, p for power, v for speed, h for height

def f(c,p):
    c=c.splitlines()
    l=len(c)
    tmp = {"_":-1,"\\":4,"/":-5," ":0}
    f=lambda x,h,v:v if'U'==c[h][x]or v<1 else f(x+(h==l-1),(h+1)%l,v+tmp[c[h][x]])
    return f(0,0,p)>0

Usage

f(16,"_/\                                         _\n   \      __       /\/\/\                  / \n    \    /  \     /      \                /  \n     \__/    \   /        \____________ _/   \n              \_/                      U     ")
f(9,"/\/\/\/\/U")

Erwan

Posted 2016-01-19T23:40:07.100

Reputation: 691

3

ES6, 117 bytes

(c,p)=>c.split`
`.map(s=>[...s.slice(0,c.match(/^.*U/m)[0].length-1)].map(c=>p+=c=='/'?-5:'    \\'.indexOf(c)))&&p>0

Ungolfed:

function hole(course, power) {
    width = course.match(/^.*U/m)[0].length - 1; // calculate width to hole
    lines = course.split("\n");
    for (i = 0; i < lines.length; i++) {
        line = lines[i].slice(0, width); // ignore extraneous parts of the course
        for (j = 0; j < line.length; j++) {
            switch (line[j]) { // accumulate remaining power
            case '/': power -= 5; break;
            case '\\': power += 4; break;
            case ' ': break;
            default: power--; break;
            }
        }
    }
    return power > 0;
}

Edit: Saved 4 bytes thanks to ՊՓԼՃՐՊՃՈԲՍԼ.

Neil

Posted 2016-01-19T23:40:07.100

Reputation: 95 035

@ՊՓԼՃՐՊՃՈԲՍԼ Thanks, I keep trying to optimise for speed... – Neil – 2016-01-21T00:15:03.950

3

JavaScript (ES6), 108 107 106 bytes

This is the solution I came up with when I created the challenge.

(p,c)=>[...(l=c.split`
`)[w=0]].map((_,i)=>l.map(t=>(g=t[i])-1|p<=0?0:p-=g>"]"?1:g>"U"?-4:g>"/"?w=1:5))&&w

Explanation

Takes the power as a number and the course as a string. Returns 1 for true or 0 for false. The course must be padded with spaces.

(p,c)=>
  [...(l=c.split`
`)                          // l = array of lines
  [w=0]]                    // w = true if the ball has entered the hole
.map((_,i)=>                // for each index i
  l.map(t=>                 // for each line t
    (g=t[i])                // g = the character at the current index
    -1|p<=0?0:              // do nothing if g is a space or the ball has no speed left
    p-=
      g>"]"?1               // case _: subtract 1 from p
      :g>"U"?-4             // case \: add 4 to p
      :g>"/"?w=1            // case U: set w to true (it doesn't matter what happens to p)
      :5                    // case /: subtract 5 from p
  )
)
&&w                         // return w

Test

var solution = (p,c)=>[...(l=c.split`
`)[w=0]].map((_,i)=>l.map(t=>(g=t[i])-1|p<=0?0:p-=g>"]"?1:g>"U"?-4:g>"/"?w=1:5))&&w
Power = <input type="number" id="power" value="16" /><br />
<textarea id="course" rows="6" cols="50">_/\                                         _
   \      __       /\/\/\                  / 
    \    /  \     /      \                /  
     \__/    \   /        \____________ _/   
              \_/                      U     </textarea><br />
<button onclick="result.textContent=solution(+power.value,course.value)">Go</button>
<pre id="result"></pre>

user81655

Posted 2016-01-19T23:40:07.100

Reputation: 10 181

2

Pyth, 35 bytes

VC.z=-Q@(1_4 5)x"_\\/"JrN6IqJ\U>Q_5

Explanation

                                    - Autoassign Q = eval(input())
                                    - Autoassign .z = rest of input
VC.z                                - For N in zip(*.z)
    =-Q                             - Q -= ...
                      JrN6          - Autoassign J to N.strip() (get rid of spaces)
       @(1_4 5)x"_\\/"              - {"_":1, "\\": -4, "/": 5, "U":5}[J] ("U" isn't defined but that's what it is according to how str.index works)
                          IqJ\U     - If J == "U"
                               >Q_5 - print Q > -5 ()

Blue

Posted 2016-01-19T23:40:07.100

Reputation: 26 661

1

JavaScript, 266 263 244 bytes

(s,a)=>{var f=(e,x)=>{for(var i=1;D=e[i][x],i<e.length;i++)if(D!=" ")return D},o=a.split(`
`),l=o.reduce((a,b)=>Math.max(a.length||a,b.length)),b="";for(i=0;i<l;i)b+=f(o,i++);for(i=0;b[i]!="U"&&s>0;i++)s-=b[i]=="_"?1:b[i]=="/"?5:-4;return s>0}

Ungolfed

(s,a)=>{
    var f=(e,x)=>{
        for(var i=1;D=e[i][x],i<e.length;i++)
            if(D!=" ")
                return D
    },
    o=a.split(`
`),
    l=o.reduce((a,b)=>Math.max(a.length||a,b.length)),
    b="";
    for(i=0;i<l;)
        b+=f(o,i++);
    for(i=0;b[i]!="U"&&s>0;i++)
        s-=b[i]=="_"?1:b[i]=="/"?5:-4;
    return s>0
}

Usage

var o = (s,a)=>{var f=(e,x)=>{for(var i=1;D=e[i][x],i<e.length;i++)if(D!=" ")return D},o=a.split(`
`),l=o.reduce((a,b)=>Math.max(a.length||a,b.length)),b="";for(i=0;i<l;)b+=f(o,i++);for(i=0;b[i]!="U"&&s>0;i++)s-=b[i]=="_"?1:b[i]=="/"?5:-4;return s>0}


o(27, `
      ____       ____ _   
   __/    \\     /    U \\  
__/        \\   /        \\_
            \\_/           `); // will return true

user49328

Posted 2016-01-19T23:40:07.100

Reputation: 11

My mistake; I thought I had copied in the first example with "27" as the first argument. I corrected this. Thank you. – user49328 – 2016-01-20T19:15:40.040

1

Ruby, 85 characters

->i,s{s.lines.map(&:bytes).transpose.any?{|o|(c=o.max)==85||i<0||!(i+=c*3%14-6)};i>0}

Adapted @manatwork's answer

Connor Clark

Posted 2016-01-19T23:40:07.100

Reputation: 51

1

Java, 219 Bytes

boolean p(int v,String c){int z=c.length(),f[]=new int[z],e,i,k;for(String r:c.split("\n"))for(i=-1;++i<r.length();)if((e=r.charAt(i))>32)f[i]=e;for(i=-1,e=0;++i<z&v>0;)v-=(k=f[i])>94?1:k>91?-4:k>84?(e=1):5;return 0<e;}
  • Flatten the course, because the y-coordinate doesn't matter, unfortunately Java doesn't have a vertical trim. It also doesn't have a String-transpose.

  • Iterate over the flattened course and keep track of the ball speed.

ECS

Posted 2016-01-19T23:40:07.100

Reputation: 361

1

Octave, 111 110 bytes

function g(v,s) A([95,47,92])=[1,5,-4];all(v>cumsum(A(m=max(cat(1,strsplit(s,'\n'){:}),[],1)))(1:find(m==85)))

Explanation:

  • Split the input on newlines and convert that annoying cell array to a matrix
  • Flatten the matrix by finding the max for each column
  • Map the characters '_/\' to [1, 5, -4] (all other characters less than '_' are mapped to 0)
  • Calculate the cumulative sum of all elements of the mapped array
  • Output True if all cumulative sums from the beginning of the course to the cup are less than the start velocity (False otherwise).

Here is a test case that I had already developed similar to the second one proposed by @Erwan and a couple of results:

s9 =
   /\
  /  \
_/    \
       \
        \
         U

g(11,s9) %False
ans = 0
g(17,s9) %True
ans =  1

And here's the first test case:

s10 = 
  _
 / U\
/    \
      \
       \
        \
         \
          \_

>> g(11,s10)
ans = 0
>> g(12,s10)
ans =  1

beaker

Posted 2016-01-19T23:40:07.100

Reputation: 2 349

i think if the course is like "//_U\\\\\\\_ the result is incorrect since you don't remove character after U same things if you have a course with local maximum like _//\\\\\U – Erwan – 2016-01-25T08:35:11.633

@Erwan But I do remove the characters after the U. That's what the (1:find(m==85)) does; it takes subarray from the first index to the location of the U. I'll check your test case with a couple of starting velocities and get back to you. – beaker – 2016-01-25T15:45:20.480

I couldn't run your solution (i don't have Octave) that why i just ask ... and because i find the issu with local maxima in the other python solution :) finally your solution work with local maxima since you use cumsum and not just sum (don't see that at the first read) – Erwan – 2016-01-25T16:25:56.190

@Erwan I've added the two test cases that you suggested. Please have a look and see if the results are what you expect. If you're trying this in MATLAB, then you're not going to be able to run it because it uses some indexing that only works in Octave. You would have to assign the result of cumsum to an intermediate variable and then use that for the final comparison all(v>tmp(1:find(m==85))). – beaker – 2016-01-25T16:34:19.850

Your solution work fine miss lots of things at the first read (just test in Matlab so many intermediate variables to add) – Erwan – 2016-01-25T16:59:04.147

@Erwan Cool, thanks for checking! – beaker – 2016-01-25T16:59:41.840

0

Python, 212 201 188 143 bytes

Much of the credit for this iteration of this script goes to @Erwan, who gave me an entirely different approach to try and some tips that saved me 55 bytes in the end.

Not recursive, so should be substantially different from the other python solution.

def g(c,p):
 o=[''.join(x).split()[0] for x in zip(*c.split('\n'))]
 t={"_":1,"/":5,"\\":-4}
 for v in o:
    if v=="U" or p<1:return p>0
    p-=t[v]

Ungolfed a bit:

def g(course,power):
  course=course.split('\n') # split into lines
  course=zip(*course) 

  #transpose and flatten course, then remove spaces
  one_line_course=[''.join(x).split[0] for x in zip(*course)] 

  terrain_values={"_":1,"/":5,"\\":-4}
  for char in one_line_course:
    if char=="U" or power<1: 
      return power>0 # true when power remains, false otherwise
    power-=terrain_values[char]

SnoringFrog

Posted 2016-01-19T23:40:07.100

Reputation: 1 709

if you want a shorter solution you can use Cyoce tip and use the transpose built-in function. somethings like that o=[''.join(x).split()[0] for x in zip(*c.split('\n'))] that win 40 bytes i think – Erwan – 2016-01-25T08:10:37.387

you can also replace break by return p>0 and remove if p... – Erwan – 2016-01-25T08:26:06.577

you need to add a condition if"U"==v or p<1 if there is a local maximum like _//\\\\\U – Erwan – 2016-01-25T08:45:59.347

@Erwan Your first tip does not work if the lines are not all the same length (short lines having trailing spaces to match long ones). Since the post said "Courses can optionally be padded with spaces" I'm not sure we can assume that that's true. I've asked about that in a comment. – SnoringFrog – 2016-01-25T17:03:52.147

yes I assume all line have the same length (egalized with white space) maybe i'm wrong in this case i think my solution is bad – Erwan – 2016-01-25T17:14:04.153

@Erwan The examples are all the same length, but the wording in the question makes me unsure. We'll wait and see what OP says. Your other tips were helpful though, I'll edit them in now – SnoringFrog – 2016-01-25T17:41:29.363

Rewrite the 'if' like this... if p<1or"U"==v – FlipTack – 2016-11-20T08:51:46.270

0

C, 629 Bytes

#include <string.h>
#include <stdlib.h>
#include <string.h>

bool swing(char *c, unsigned int p)
{
    char *olc = calloc(strlen(c), 1);
    int x = 0;
    char *n = c;

    while(1) {
        if(*n == '\0')  break;
        else if(*n == ' ') x += 1;
        else if(*n == '\n') x = 0;
        else {
            olc[x] = *n;
            x += 1;
        }
        n++;
    }

    int hd = 0;
    for(char *i = olc; i != strchr(olc, 'U'); i++) {
        if(*i == '_') hd += 1;
        else if(*i == '/') hd += 5;
        else hd -= 4;
    }

    free(olc);
    if(hd < p) return 1;
    return 0;
}

Ungolfed:

bool swing(char *course, unsigned int power)
{
    const size_t course_len = strlen(course);
    char *one_line_course = calloc(course_len, sizeof(char));
    assert(one_line_course);
    int x_pos = 0;
    char *next = course;

    //Convert to one line representation
    while(1) {
        if(*next == '\0') {
            break;
        }
        else if(*next == ' ') {
            x_pos += 1;
        }
        else if((*next == '\n') || (*next == '\r')) {
            x_pos = 0;
        }
        else {
            one_line_course[x_pos] = *next;
            x_pos += 1;
        }
        next++;
    }

    //Calculate power vs distance
    const char *hole_location = strchr(one_line_course, 'U');
    int hole_distance = 0;
    for(char *i = one_line_course; i != hole_location; i++) {
        if(*i == '_') {
            hole_distance += 1;
        }
        else if(*i == '/') {
            hole_distance += 5;
        }
        else {
            hole_distance -= 4;
        }
    }

    free(one_line_course);
    if(hole_distance < power) {
        return true;
    }
    else {
        return false;
    }
}

Basically I just make one pass to convert the input string to fit everything in one line, then

samt1903

Posted 2016-01-19T23:40:07.100

Reputation: 1

Welcome to Programming Puzzles & Code Golf! You can (and should) be able to reduce the size substantially by eliminating most whitespace; you can reduce some of your if/else e.g. x+=*n==' ')?1:*n=='\n'?-x:(olc[x]=*n,1. Another tip: in C, unsigned int can be written unsigned, saving 4 bytes right away. – Toby Speight – 2016-01-25T22:09:08.693