(inspired by this challenge over on Puzzling -- SPOILERS for that puzzle are below, so stop reading here if you want to solve that puzzle on your own!)

If a letter in a word occurs alphabetically later than the previous letter in the word, we call that a rise between the two letters. Otherwise, including if it's the same letter, it's called a fall.

For example, the word ACE has two rises (A to C and C to E) and no falls, while THE has two falls (T to H and H to E) and no rises.

We call a word Bumpy if the sequence of rises and falls alternates. For example, BUMP goes rise (B to U), fall (U to M), rise (M to P). Note that the first sequence need not be a rise -- BALD goes fall-rise-fall and is also Bumpy.

The challenge

Given a word, output whether or not it's Bumpy.


  • A word (not necessarily a dictionary word) consisting of ASCII alphabet ([A-Z] or [a-z]) letters only, in any suitable format.
  • Your choice if the input is all uppercase or all lowercase, but it must be consistent.
  • The word will be at least 3 characters in length.


A truthy/falsey value for whether the input word is Bumpy (truthy) or not Bumpy (falsey).

The Rules

  • Either a full program or a function are acceptable.
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.







Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes

var QUESTION_ID=93005,OVERRIDE_USER=42963;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • \$\begingroup\$ Dangit. This would be easier if same letter was neither rise nor fall. \$\endgroup\$
    – mbomb007
    Commented Sep 12, 2016 at 14:55
  • \$\begingroup\$ I don't understand provided examples: if BUMP is listed in Truthy (i.e. Bumpy), why BUMPY is in the Falsey list? What does "rises and falls alternates" means? Two rise s cannot be successively? \$\endgroup\$
    – VolAnd
    Commented Sep 13, 2016 at 6:59
  • 4
    \$\begingroup\$ @VolAnd Yes, it means that a rise is always followed by a fall and vice versa. BUMPY is falsy because MPY gives two consecutive rises. In other words, no substring of length 3 must be sorted ascendingly or descendingly for a word to be bumpy (apart from the special case where two consecutive letters are identical). \$\endgroup\$ Commented Sep 13, 2016 at 7:36
  • \$\begingroup\$ Can you spoiler the answer to the Puzzling.SE question so others who wish to solve it themselves can do so? \$\endgroup\$
    – AAM111
    Commented Sep 14, 2016 at 0:44
  • 1
    \$\begingroup\$ @OldBunny2800 I won't put a full spoiler in (I don't want to make my challenge here hard to read by hiding crucial information behind a spoiler), but I'll add some additional text to the top as a warning. Thanks! \$\endgroup\$ Commented Sep 14, 2016 at 0:51

30 Answers 30


MATL, 4 bytes



d     % Implicitly take input. Take difference between each element
 0>   % Check whether diff's are positive. Should result in [0 1 0 1 ...] pattern.
   d  % Again take the difference. Any consecutive rises or falls results in a 
      % difference of 0, which is a falsy value in MATL

This is my first MATL entry, so I wonder how much improvement there can be from this naive port from my MATLAB/Octave attempt (which would be @(a)all(diff(diff(a)>0))). Note that the all is not necessary because any zero makes an array false, so there's no A in the MATL port.

  • \$\begingroup\$ See the corrected challenge. There was a typo in a test case. Your approach was correct. In fact, d0>d should work (you don't need the A as per our definition of truthy/falsey) \$\endgroup\$
    – Luis Mendo
    Commented Sep 12, 2016 at 15:00
  • 1
    \$\begingroup\$ Nice work, outgolfing Luis in his own language! I've tried before, and that's no easy task. ;) \$\endgroup\$
    – DJMcMayhem
    Commented Sep 12, 2016 at 15:06
  • \$\begingroup\$ @DJMcMayhem Haha. That's what I get for reading the challenge too quickly. In my defense, it's counter-intuitive that two equal letters are a fall. And the (now corrected) misleading test cases didn't help either :-) \$\endgroup\$
    – Luis Mendo
    Commented Sep 12, 2016 at 15:08
  • 1
    \$\begingroup\$ @DJMcMayhem Thanks - although maybe I just got lucky, because I didn't actually think about consecutive equal letters, but that turned out to be exactly what was asked... \$\endgroup\$
    – Sanchises
    Commented Sep 12, 2016 at 15:14
  • 1
    \$\begingroup\$ @immibis In MATL(AB) and Octave, yes. See this Meta answer. \$\endgroup\$
    – Sanchises
    Commented Sep 14, 2016 at 7:22

JavaScript (ES6), 75 69 63 46 43 bytes

Saved 3 bytes thanks to Neil:


Destructuring the string parameter instead of s.slice(1).

Previous solution:
Saved 17 bytes thanks to ETHproductions:


What happened from the previous solution step by step:

f=(s,i=0,a=s[i++]<s[i])=>s[i+1]&&(b=a^(a=s[i]<s[i+1]))?f(s,i):b // (63) Original
f=(s,i=0,a=s[i++]<s[i])=>s[i+1]&&(b=a^(s[i]<s[i+1]))?f(s,i):b   // (61) No point in reassigning `a`, it's not used again
f=(s,i=0,a=s[i++]<s[i])=>s[i+1]&&(b=a^s[i]<s[i+1])?f(s,i):b     // (59) Remove unnecessary parentheses
f=(s,i=0)=>s[i+2]&&(b=s[i++]<s[i]^s[i]<s[i+1])?f(s,i):b         // (55) `a` is now just a waste of bytes
f=(s,i=0)=>s[i+2]?(b=s[i++]<s[i]^s[i]<s[i+1])?f(s,i):b:1        // (56) Rearrange conditional expressions to allow for more golfing
f=(s,i=0)=>s[i+2]?(b=s[i++]<s[i]^s[i]<s[i+1])&&f(s,i):1         // (55) Rearrange conditional expression
f=(s,i=0)=>s[i+2]?(s[i++]<s[i]^s[i]<s[i+1])&&f(s,i):1           // (53) `b` is now also a waste of bytes
f=(s,i=0)=>s[i+2]?s[i++]<s[i]^s[i]<s[i+1]&&f(s,i):1             // (51) Remove unnecessary parentheses
f=s=>s[2]?s[0]<s[1]^s[1]<s[2]&&f(s.slice(1)):1                  // (46) Use `s.slice(1)` instead of `i`

Previous solutions:
63 bytes thanks to ETHproductions:


69 bytes:


75 bytes:

f=(s,a=s[0]<s[1])=>{for(i=1;i+1<s.length&&(b=a^(a=s[i++]<s[i])););return b}

All letters in a word must have the same case.

  • 2
    \$\begingroup\$ You can golf it down quite a bit further: github.com/ETHproductions/golf/blob/gh-pages/misc/93014.js \$\endgroup\$ Commented Sep 12, 2016 at 15:36
  • \$\begingroup\$ @ETHproductions Should I post the content of your link ? \$\endgroup\$
    – Hedi
    Commented Sep 12, 2016 at 16:10
  • \$\begingroup\$ You can if you'd like :-) \$\endgroup\$ Commented Sep 12, 2016 at 16:18
  • \$\begingroup\$ Can !s[2]|... do the same as s[2]?...:1? \$\endgroup\$
    – Titus
    Commented Sep 12, 2016 at 18:20
  • 1
    \$\begingroup\$ Sorry for being late to the party, but for 43 bytes I give you: f=([c,...s])=>s[1]?c<s[0]^s[0]<s[1]&&f(s):1 \$\endgroup\$
    – Neil
    Commented Sep 16, 2016 at 8:51

LabVIEW, 36 equivalent bytes

Golfed down using logical equivalences:




First we convert to lowercase, then to byte array. Trim off the first element of the byte array as it has no precedent. Then, for each element in the array, check if it's greater than the previous one (U8 char maps to ASCII as you expect) and store the result for the next iteration, as well as in an array for viewing overall bumpiness. If the current and prior boolean check are equal, we terminate the loop and it's not bumpy. Else, it's bumpy!

  • 1
    \$\begingroup\$ What a cool language! Welcome to PPCG! \$\endgroup\$
    – DJMcMayhem
    Commented Sep 13, 2016 at 1:31
  • 1
    \$\begingroup\$ Thanks! I'll never compete with the 4 byte answers, but it's a nice way to improve my skills :) \$\endgroup\$ Commented Sep 13, 2016 at 1:34
  • \$\begingroup\$ See here. Your scoring is definitely wrong and overly excessive. I don't think your answer is really 246450 - 246549 bytes. \$\endgroup\$ Commented Sep 13, 2016 at 12:20
  • \$\begingroup\$ I was going off the Memory tab, as I didn't know there was a concept of equivalent bytes for LabVIEW. Will count them up and edit answer later today. \$\endgroup\$ Commented Sep 13, 2016 at 12:23
  • 1
    \$\begingroup\$ @Erik I'm on Firefox on Windows, but opening it on mobile also breaks things. Just meta.ppcg.lol works. Anyway, this is out of scope for comments. \$\endgroup\$
    – anon
    Commented Sep 14, 2016 at 14:19

Python, 56 bytes

lambda s:all((x<y)^(y<z)for x,y,z in zip(s,s[1:],s[2:]))

All test cases are at ideone

Zips through triples of characters in s and tests that all such triples have left and right pairs with different rise/fall properties.
Works for either all uppercase or all lowercase.


Ruby, 57 48 bytes

Expects input to be all uppercase.


See it on repl.it: https://repl.it/D7SB


The regular expression /.(?=(.)(.))/ matches each character that's followed by two more characters. (?=...) is a positive lookahead, meaning we match the subsequent two characters but don't "consume" them as part of the match. Inside the curly braces, $& is the matched text—the first character of the three—and $1 and $2 are the captured characters inside the lookahead. In other words, if the string is "BUMPY", it will first match "B" (and put it in $&) and capture "U" and "M" (and put them in $1 and $2). Next it will match "U" and capture "M" and "P", and so on.

Inside the block we check if the first pair of characters ($& and $1) is a rise and the second ($1 and $2) is a fall or vice versa, much like most of the other answers. This ^ expression returns true or false, which gets converted to a string and inserted in place of the match. As a result, our example "BUMPY" becomes this:


Since we know the input is all uppercase, we know "f" will only occur as part of "false" and !result[?f] gives us our answer.

  • \$\begingroup\$ How does it work? \$\endgroup\$ Commented Sep 13, 2016 at 8:44
  • 1
    \$\begingroup\$ @GreenAsJade I've added an explanation to my answer. \$\endgroup\$
    – Jordan
    Commented Sep 13, 2016 at 12:45

C#, 64 63 55 bytes

unsafe bool B(char*s)=>1>s[2]||*s<s[1]!=*++s<s[1]&B(s);

-8 bytes from Scepheo's suggestions

This is a port of Hedi's solution to C#. I also came up with a recursive solution, but the recursion wasn't as good. My original solution is below.

My Original C#, 96 94 91 bytes

unsafe bool B(char*s,bool f=1>0,int i=0)=>1>s[1]||(s[0]<s[1]?f:1>i?!(f=!f):!f)&B(s+1,!f,1);

-2 bytes by using 1>0 instead of true.

-3 bytes from Scepheo's suggestions for the port solution above

Calls itself recursively checking that the rising/falling alternates each time.


// unsafe in order to golf some bytes from string operations.
// f alternates with each recursive call
// i is 0 for the first call, 1 for all subsequent calls
unsafe bool B(char* s, bool f = 1 > 0, int i = 0) =>
    1 > s[1] ? 1 > 0// (instead of 1 == s.Length) check if s[1] = NULL, and return true.
    : (
        s[0] < s[1] ? f // Rising, so use f...
        : // Else falling
            1 > i ? !(f=!f) // But this is the first call, so use true (but flip f)...
            : !f // Not first call, so use !f...
    & B(s+1, !f, 1) // ...AND the previous value with a recursive call
                    // s+1 instead of s.Substring(1)
  • \$\begingroup\$ Seems like the last one can do without the ?: operator or parentheses: unsafe bool B(char*s)=>1>s[2]|s[0]<s[1]!=s[1]<s[2]&B(s+1); \$\endgroup\$
    – Scepheo
    Commented Sep 13, 2016 at 13:19
  • \$\begingroup\$ Actually, although I can't test this, manipulating the pointer itself seems even terser: unsafe bool B(char*s)=>1>s[2]|*s<s[1]!=*++s<s[1]&B(s); \$\endgroup\$
    – Scepheo
    Commented Sep 13, 2016 at 13:36
  • \$\begingroup\$ @Scepheo I got StackOverflowExceptions with those suggestions, but they work using boolean OR || instead of bitwise OR |. Updated the post, thanks. \$\endgroup\$
    – milk
    Commented Sep 13, 2016 at 19:26

C 59 Bytes

  • \$\begingroup\$ The solution in 70 bytes returns 1 (True) for case AAA - the first "Falsey" in the examples \$\endgroup\$
    – VolAnd
    Commented Sep 13, 2016 at 5:31
  • \$\begingroup\$ I'm testing using gcc (GCC) 3.4.4 (cygming special, gdc 0.12, using dmd 0.125) and I get false for aaa and excited. In this version non-zero is falsey, and zero is truthy. Actually wondering now if this is allowed. \$\endgroup\$
    – cleblanc
    Commented Sep 13, 2016 at 13:44
  • \$\begingroup\$ Call f("ABCDEFGHIJKLMNOPQRSTUVWXYZ") compiled in Visual Studio 2012 returns value 23 that can be treated as True but in the question this value is in section "Falsey", so value 0 expected. \$\endgroup\$
    – VolAnd
    Commented Sep 13, 2016 at 14:01
  • \$\begingroup\$ I misunderstood what was allowed for True and Falsey. Now I've read the that post and it seems clear what the values must be for "C". \$\endgroup\$
    – cleblanc
    Commented Sep 13, 2016 at 14:20
  • \$\begingroup\$ Here's our standard definitions for truthy and falsey, based on Meta consensus. \$\endgroup\$ Commented Sep 13, 2016 at 19:04

Jelly, 6 bytes


Based on @sanchises' answer.

Try it online! or Verify all.


OI>0IẠ  Input: string S
O       Convert each char in S to an ordinal
 I      Get the increments between each pair
  >0    Test if each is positive, 1 if true else 0
    I   Get the increments between each pair
     Ạ  Test if the list doesn't contain a zero, 1 if true else 0

JavaScript (ES6), 65 bytes


.map is definitely not the best solution.


Python 2, 88 bytes

Simple solution.

m=map(lambda x,y:y>x,s[:-1],s[1:])
print all(x-y for x,y in zip(m[:-1],m[1:]))

Try it online

If same letters in a row were neither a rise nor a fall, the solution would be 79 bytes:

print all(x-y for x,y in zip(m[:-1],m[1:]))

Perl, 34 bytes

Includes +3 for -p (code contains ' so -e can't be used)

Give uppercase input on STDIN:

bump.pl <<< AAA


#!/usr/bin/perl -p
s%.%$&.z lt$'|0%eg;$_=!/(.)\1./

Python, 51 bytes

g=lambda a,b,c,*s:((a<b)^(b<c))*(s==()or g(b,c,*s))

Takes input like g('B','U','M','P') and outputs 1 or 0.

Uses argument unpacking to take the first three letters and check if the first two compare differently from the second two. Then, recurses on the remainder, using multiplication for and.

  • \$\begingroup\$ Nice input golf. ;-) \$\endgroup\$ Commented Sep 13, 2016 at 12:53

Jelly, 6 5 bytes

-1 byte thanks to @Dennis (use a cumulative reduction)


All test cases are at TryItOnline


<2\IẠ - main link takes an argument, s,    e.g. "BUMP"    or "BUMPY"
<    - less than comparison (a dyad)
 2   - literal 2 (a nilad)
  \  - n-wise overlapping reduction when preceded by a dyad-nilad chain
       (i.e. reduce the list by pairs with less than)
                                           e.g. [1,0,1]   or [1,0,1,1]
   I  - consecutive differences,           e.g. [-1,1]    or [-1,1,0]
    Ạ - All, 0 if any values are 0 else 1, e.g. 1         or 0

Works for either all uppercase or all lowercase.


Japt, 8 bytes

Uä> ä- e

Test it online!

How it works

Uä> ä- e  // Implicit: U = input string
Uä>       // Map each pair of chars X, Y in U to X > Y.
    ä-    // Map each pair of items in the result to X - Y.
          // If there are two consecutive rises or falls, the result contains a zero.
       e  // Check that every item is truthy (non-zero).
          // Implicit: output last expression
  • \$\begingroup\$ Same as my solution. Except 11x shorter. :P \$\endgroup\$
    – mbomb007
    Commented Sep 12, 2016 at 15:18

C# 105 104 Bytes

bool f(char[]x){int t=1;for(int i=2,p=x[1],f=x[0]-p>>7;i<x.Length;)f^=t&=p<(p=x[i++])?1-f:f;return t>0;}

105 bytes Solution:

bool f(char[]x){bool t=1>0,f=x[0]<x[1];for(int i=2,p=x[1];i<x.Length;)f^=t&=p<(p=x[i++])?!f:f;return t;}

Try it online

Using an array of chars saved one byte since the space can be omitted after the brackets. f(string x) vs f(char[]x)

It is 101 bytes if I can return an int 1/0 instead of bool true/false

int f(char[]x){int t=1;for(int i=2,p=x[1],f=x[0]-p>>7;i<x.Length;)f^=t&=p<(p=x[i++])?1-f:f;return t;}

Haskell, 52 bytes

f x=and$g(/=)$g(>)x
  where g h y=zipWith h(tail y)y

I suspect I could get this a chunk smaller if I managed to get rid of the "where" construct, but I'm probably stuck with zipWith.

This works by making a list of the rises (True) and falls (False), then making a list of if the ajacent entries in this list are different

This is my first attempt at one of these, so I'll go through my thought process in case I've gone horribly wrong somewhere.

Ungolfed Version (168 bytes)

isBumpy :: [Char] -> Bool
isBumpy input = and $ areBumps $ riseFall input
    riseFall ax@(x:xs) = zipWith (>) xs ax
    areBumps ax@(x:xs) = zipWith (/=) xs ax

Shorten names, remove type information (100 bytes)

f x = and $ g $ h x
    h ax@(x:xs) = zipWith (>) xs ax
    g ax@(x:xs) = zipWith (/=) xs ax

Move h into the main function as it is only used once (86 bytes)

f ax@(x:xs) = and $ g $ zipWith (>) xs ax
    g ax@(x:xs) = zipWith (/=) xs ax

Realise that areBumps and riseFall are similar enough to abstract (73 bytes)

f x  = and $ g (/=) $ g (>) x
    g h ya@(y:ys) = zipWith h ys ya

Note that (tail y) is shorter than ya@(y:ys) (70 bytes)

f x  = and $ g (/=) $ g (>) x
    g h y = zipWith h (tail y) y

Tidy up; remove unneeded spaces (52 bytes)

f x=and$g(/=)$g(>)x
  where g h y=zipWith h(tail y)y
  • \$\begingroup\$ ... and I've just noticed a shorter Haskell answer that was posted before mine that does basically the same thing. I am terrible at spotting things. \$\endgroup\$
    – Teron
    Commented Sep 14, 2016 at 17:12
  • \$\begingroup\$ You mean the one that doesn't work? ;-) You may use g h=tail>>=zipWith h and make it a global function to avoid the where keyword. \$\endgroup\$ Commented Sep 19, 2016 at 13:28
  • \$\begingroup\$ @ChristianSievers Fixed it, and I just noticed this answer which now does exactly the same thing as mine, rendering my answer better suited as a comment to this one. \$\endgroup\$
    – BlackCap
    Commented Sep 19, 2016 at 15:47

Java 7, 157 153 150 125 117 bytes

int c(char[]z){for(int i=2,a,b,c;i<z.length;i++)if(((a=z[i-1])<(c=z[i])&(b=z[i-2])<a)|(a>=c&b>=a))return 0;return 1;}

Ungolfed & test cases:

Try it here.

class M{
  static int c(char[] z){
    for(int i = 2, a, b, c; i < z.length; i++){
      if(((a = z[i-1]) < (c = z[i]) & (b = z[i-2]) < a) | (a >= c & b >= a)){
        return 0; //false
    return 1; //true

  public static void main(String[] a){
    System.out.print(c("ABA".toCharArray()) + ", ");
    System.out.print(c("ABB".toCharArray()) + ", ");
    System.out.print(c("BAB".toCharArray()) + ", ");
    System.out.print(c("BUMP".toCharArray()) + ", ");
    System.out.print(c("BALD".toCharArray()) + ", ");
    System.out.print(c("BALDY".toCharArray()) + ", ");
    System.out.print(c("UPWARD".toCharArray()) + ", ");
    System.out.print(c("EXAMINATION".toCharArray()) + ", ");
    System.out.print(c("AZBYCXDWEVFUGTHSIRJQKPLOMN".toCharArray()) + ", ");

    System.out.print(c("AAA".toCharArray()) + ", ");
    System.out.print(c("ACE".toCharArray()) + ", ");
    System.out.print(c("THE".toCharArray()) + ", ");
    System.out.print(c("BUMPY".toCharArray()) + ", ");
    System.out.print(c("BALDING".toCharArray()) + ", ");
    System.out.print(c("ABCDEFGHIJKLMNOPQRSTUVWXYZ".toCharArray()) + ", ");


1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
  • \$\begingroup\$ @TimmyD Hmm, so it's rise when a > b, but fall when a <= b, instead of > and <? \$\endgroup\$ Commented Sep 12, 2016 at 14:51
  • \$\begingroup\$ @TimmyD Ok, it's fixed, and even saves 3 bytes. :) \$\endgroup\$ Commented Sep 12, 2016 at 14:58
  • 1
    \$\begingroup\$ you can redefine your method to accept char[] so you dont have to transform your input string to char array. that should save a few bytes. PS: java ftw! \$\endgroup\$
    – peech
    Commented Sep 12, 2016 at 15:01
  • 1
    \$\begingroup\$ Did you perhaps means to change String s --> char[]z? \$\endgroup\$
    – user18932
    Commented Sep 13, 2016 at 13:54
  • 1
    \$\begingroup\$ You can return a truthy or falsey value, so make your method an int and return 1 or 0 :).. Puts you down to 117 bytes \$\endgroup\$
    – Shaun Wild
    Commented Sep 14, 2016 at 8:46

PowerShell v2+, 83 bytes


A little bit of a different approach. This loops through the input $n, each iteration seeing whether the previous character $n[$_-1] is -lessthan the current character $n[$_], then casting the result of that Boolean operator to an int with +. Those are -joined together into a string, stored into $a. We then check whether $a is -equal to $a with any substrings of 00 or 11 removed.


Python 2.7, 84 bytes

for i in range(len(s)-1):o&=(s[i]<s[i+1])==b;b^=1
print o

Returns 1 for bumpy, 0 for otherwise

Learned some cool stuff with bitwise & and ^.
Starts with boolean b defining first pair as up/down, then tests and flips b for each following pair.
o flips to false if test fails and sticks.
Requires quotes around input (+4 bytes for raw_input() if that breaks some rule)

Test It


Python 2.7 (again, 84 83 bytes)

def a(s):x=s[1:];return[cmp(s[0],x)]+a(x) if x else []
print len(set(a(input())))>1

Or, 78 77 bytes without the print.

By the way, the above 56 byte Python 2.7 example breaks on, for example, "abbab" or any other input with repeated characters. Never mind, didn't read instructions. Revising.

Okay, down to 83. The triples one is nicer though.

  • \$\begingroup\$ Here's some tips for ya. 1. Remove some whitespace a(x)if x else[]. 2. Use a lambda instead a=lambda s:[cmp(s[0],s[1:])]+a(s[1:])if s[1:]else[] 3. Use a lambda at the end instead of printing. lambda s:len(set(a(s)))>1 4. if len(set(a(s))) isn't greater than 1, than it's already falsy, so you can take off >1 \$\endgroup\$
    – DJMcMayhem
    Commented Sep 13, 2016 at 2:12

05AB1E, 9 bytes



SÇ          # convert to list of ascii values
  ¥         # take delta's
   0›       # check if positive, giving a list of 1's and 0's
            # if they alternate, the word is bumpy
     ¥      # take delta's again, if we have any 0's in the list the word is not bumpy
      _     # logical negation, turning 0 into 1 and everything else to 0
       O    # sum, producing 0 for a bumpy word and 1 for a non-bumpy word
        _   # logical negation, inverting the previous 1 into 0 and vice versa

Try it online!


CJam, 15 bytes


Try it online! (As a linefeed-separated test-suite.)


l    e# Read input.
2ew  e# Get all (overlapping) pairs.
::<  e# Check whether each pair is strictly ascending (1) or not (0).
2ew  e# Get all (overlapping) pairs.
::^  e# Take the bitwise XOR of each pair, giving 1 if a rise and a fall alternate,
     e# and zero if there are two rises or two falls in succession.
:*   e# Product. Gives 1 only if the previous step yielded a list of 1s, meaning
     e# that any two consecutive rises/falls will turn this into a zero.

PHP, 80 bytes




empty output for false, 1 for true

or Hedi´s recursive approach ported and a little golfed for 70 bytes:

function f($s){return!$s[2]|$s[0]<$s[1]^$s[1]<$s[2]&&f(substr($s,1));}

Actually, this should recurse infinitely for bumpy words, but it does not!


Haskell, 30 37 bytes

q f=tail>>=zipWith f;k=and.q(/=).q(>)



  • \$\begingroup\$ That doesn't accept "bald", foldl1(/=) doesn't do what you think it does. \$\endgroup\$ Commented Sep 19, 2016 at 13:18
  • \$\begingroup\$ @ChristianSievers Auch, you're right. Thanks for the heads up \$\endgroup\$
    – BlackCap
    Commented Sep 19, 2016 at 15:33

PHP 7, 137 118 bytes


Empty output for Bumpy, 0 for Not Bumpy.

This is my first attempt at code golfing and I have to improve a lot, but it was a wonderful method to learn new things for me. I also wanted to challenge myself on that task by using the new PHP 7 Spaceship Operator which seems very interesting.

Anyway I'm not satisfied about it, first of all for the fact that I had to add an extra if(isset($s[$i+2])) to check if the variable exist because I did not find another workaround to the problem, but this is it for now. (Note: I fixed that simply by changing strlen($s)-1 to strlen($s)-2, I couldn't really see that before...).

Testing code:

$as = array("ABA", "ABB", "BAB", "BUMP", "BALD", "BALDY", "UPWARD", 

foreach ($as as $s) {

Test online

  • \$\begingroup\$ Hello, and welcome to PPCG! Great first post! \$\endgroup\$ Commented Sep 16, 2016 at 16:13
  • \$\begingroup\$ Welcome to PPCG! Nice first post. Check out Tips for PHP for some additional golfing suggestions. \$\endgroup\$ Commented Sep 16, 2016 at 16:29

Javascript ES6, 100 bytes

d="charCodeAt";a=b=>{f=r=0;for(i=1;i<b.length;i++){if(b[d](i)<=b[d](i-1)){f=1}else{r=1}}return f&&r}

Try it here:

d="charCodeAt";a=b=>{f=r=0;for(i=1;i<b.length;i++){if(b[d](i)<=b[d](i-1)){f=1}else{r=1}}return f&&r}

Oh come on two people already beat me to it by 40 bytes... whatever

  • \$\begingroup\$ Hint: "A"<"B", so you don't need to get the chars' charcodes. \$\endgroup\$ Commented Sep 12, 2016 at 15:42
  • \$\begingroup\$ Also, this returns 1 for BUMPY (or anything else that contains both a rise and a fall). \$\endgroup\$ Commented Sep 12, 2016 at 15:57
  • \$\begingroup\$ This doesn't seem to quite work right. \$\endgroup\$ Commented Sep 13, 2016 at 12:39

Python 3, 148 139 127 bytes

def _(w):*r,=map(lambda a,b:0>ord(a)-ord(b)and-1or 1,w,w[1:]);s=len(r)%2==0and r+[r[0]]or r;return sum(s)in(-1,1)and s==s[::-1]

testing code


for w in positives:
    print(_(w), w)
    assert _(w)

for w in negatives:
    print(_(w), w)
    assert not _(w)
  • \$\begingroup\$ i really suck at this #facepalm \$\endgroup\$
    – Jeffrey04
    Commented Sep 13, 2016 at 2:33
  • \$\begingroup\$ Welcome to PPCG! Check out Tips for Golfing in Python, and take inspiration from the other answers. \$\endgroup\$ Commented Sep 13, 2016 at 12:55

C, 65 57 60 bytes

 r;f(char*s){for(r=0;*++s&&(r=~r&1<<(*s>*(s-1))););return r;}

is fix of

r;f(char*s){for(;*++s&&(r=~r&1<<(*s>*(s-1))););return r;}

that works correctly with any data only at single function call (when the global variable r is initialized to zero).

But in any case this is shorter than previous solution (65 bytes) due to use of for instead of while. But previous (the following) is a little easier to understand:

r;f(char*s){while(*++s)if(!(r=~r&1<<(*s>*(s-1))))break;return r;}

My solution is based on bitwise & with inverted previous and current direction code, where direction code can be 2 (1<<1) for character code increase (*s > *(s-1)) or 1 (1<<0) otherwise. Result of this operation became 0 if we use the same direction code as previous and current, i.e. when word is not Bumpy.


Code for testing:

#include <stdio.h>
#include <string.h>

r;f(char*s){for(;*++s&&(r=~r&1<<(*s>*(s-1))););return r;}

int main(void)
    char * Truthy[] = { "ABA", 
                        "AZBYCXDWEVFUGTHSIRJQKPLOMN" };
    char * Falsey[] = { "AAA",
    int posTestNum = sizeof(Truthy) / sizeof(char *);
    int negTestNum = sizeof(Falsey) / sizeof(char *);
    int i;
    int rate = 0;
    int tests = 0;
    int res = 0;
    printf("Truthy (%d tests):\n", posTestNum);
    for (i = 0; i < posTestNum; i++)
        printf("%s - %s\n", Truthy[i], f(Truthy[i]) ? (rate++, "OK") : "Fail");
        r = 0;
    printf("\nFalsey (%d tests):\n", negTestNum);
    for (i = 0; i < negTestNum; i++)
        printf("%s - %s\n", Falsey[i], f(Falsey[i]) ? "Fail" : (rate++, "OK"));
        r = 0;
    printf("\n%d of %d tests passed\n", rate, tests);
    return 0;
  • \$\begingroup\$ Per meta consensus, functions have to be reusable. That means you cannot reset r to 0 for free, but must do so from within the function. \$\endgroup\$
    – Dennis
    Commented Sep 15, 2016 at 20:00
  • \$\begingroup\$ @Dennis You're right, initialization is required, but only for repeated calls. Let's assume that for a single call that works with any data because compiler provide initialisation for global variables \$\endgroup\$
    – VolAnd
    Commented Sep 16, 2016 at 4:28
  • \$\begingroup\$ I think you should make the 60 byte solution your main one, since the 57 byte version isn't valid by that meta post I cited. \$\endgroup\$
    – Dennis
    Commented Sep 16, 2016 at 4:49
  • \$\begingroup\$ @Dennis Done! +3 bytes \$\endgroup\$
    – VolAnd
    Commented Sep 16, 2016 at 7:47

PHP, 100 bytes

for($s=$argv[1];$i<strlen($s)-1;$i++)$s[$i]=$s[$i+1]>$s[$i]?r:f;echo str_replace([rr,ff],'',$s)==$s;

Replaces every char of the string (except the last one obviously) with an r for rise or an f for fall and then checks whether rr or ff occur in the string. To avoid that the last remaining character interfers with that, input must be all uppercase.

I'm very unsatisfied with the loop, for example I have a feeling that there must be a way to combine the $i++ into one of the several $is used in the loop, but I failed to find that way. Maybe someone else sees it.

(That's also why I posted my code, despite it being 20 (!) bytes longer than Titus' nice solution.)


Java 8, 114 90 bytes

(c,b)->{b=c[0]<c[1];for(int i=2;i<c.length;i++)if(c[i]>c[i-1]!=(b=!b))return 0;return 1;};

Ungolfed test program

public static void main(String[] args) {
    BiFunction<char[], Boolean, Integer> func = (c, b) -> {
        b = c[0] < c[1];
        for (int i = 2; i < c.length; i++) {
            if (c[i] > c[i - 1] != (b = !b)) {
                return 0;
        return 1;

    System.out.println(func.apply("ABCDEFG".toCharArray(), false));
    System.out.println(func.apply("AZBYCXDGEF".toCharArray(), false));
    System.out.println(func.apply("ZXZXZX".toCharArray(), false));
    System.out.println(func.apply("ZXCYZ".toCharArray(), false));
    System.out.println(func.apply("AAA".toCharArray(), false));

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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