Programming Puzzles & Code Golf Stack Exchange is a question and answer site for programming puzzle enthusiasts and code golfers. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

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!

share|improve this question
1  
If 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 Jan 20 at 0:09
1  
This is really more of a fixed-track mechanism than a golf course :P – Zach Gates Jan 20 at 1:27
23  
I like that \/\/\/\/\/ is a more efficient course than __________. – ezrast Jan 21 at 0:53
2  
That's what I was thinking, 4 down, 5 up, then .5 must be average. Oh, flat is 1? – Leif Willerts Jan 21 at 13:21
    
Will each line in a course always be the same length (with trailing spaces filling in the end of shorter lines)? – SnoringFrog Jan 25 at 17:02

16 Answers 16

up vote 17 down vote accepted

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.
share|improve this answer
    
I might be missing something, but does it stop at Q? Ie the last example could cause some issues? – flindeberg Jan 20 at 14:01
    
@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 Jan 20 at 22:45

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!

share|improve this answer

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 
share|improve this answer

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.

share|improve this answer
    
I think `` should be positive (description looks wrong) – isaacg Jan 20 at 5:57
    
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 Jan 20 at 6:13
    
I think I fixed the problem. – Mama Fun Roll Jan 20 at 6:40
    
That's a cool button ;) – J Atkin Jan 20 at 14:32
    
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 Jan 20 at 21:54

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.
share|improve this answer

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.

share|improve this answer
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 Jan 21 at 14:27
1  
Also welcome to PPCG, I'm really glad to see you here! :) (And using Retina as well.) – Martin Ender Jan 21 at 14:28
    
Sure! It was on the hot question list and looked fun. I thought I'd give it a try :-) – Kobi Jan 21 at 14:29

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 ՊՓԼՃՐՊՃՈԲՍԼ.

share|improve this answer
    
@ՊՓԼՃՐՊՃՈԲՍԼ Thanks, I keep trying to optimise for speed... – Neil Jan 21 at 0:15

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>

share|improve this answer

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")
share|improve this answer

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 ()
share|improve this answer

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

share|improve this answer

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
share|improve this answer
    
My mistake; I thought I had copied in the first example with "27" as the first argument. I corrected this. Thank you. – user49328 Jan 20 at 19:15

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.

share|improve this answer

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
share|improve this answer
    
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 Jan 25 at 8:35
    
@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 Jan 25 at 15:45
    
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 Jan 25 at 16:25
    
@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 Jan 25 at 16:34
    
Your solution work fine miss lots of things at the first read (just test in Matlab so many intermediate variables to add) – Erwan Jan 25 at 16:59

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

share|improve this answer
    
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 Jan 25 at 22:09

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]
share|improve this answer
    
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 Jan 25 at 8:10
    
you can also replace break by return p>0 and remove if p... – Erwan Jan 25 at 8:26
    
you need to add a condition if"U"==v or p<1 if there is a local maximum like _//\\\\\U – Erwan Jan 25 at 8:45
    
@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 Jan 25 at 17:03
    
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 Jan 25 at 17:14

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.