Yahtzee Small Straight Detection

In the game Yahtzee, players roll five six-sided dice, and attempt to create certain hands to score points. One such hand is a small straight: four consecutive numbers, not necessarily in order. The three possible small straights are 1, 2, 3, 4, 2, 3, 4, 5, and 3, 4, 5, 6.

For example, [3, 5, 6, 1, 4] contains the small straight [3, 4, 5, 6].

Input

An unsorted list of five integers, each between 1 and 6 inclusive, representing a Yahtzee hand.

Output

A truthy value if the hand contains a small straight and a falsy value otherwise.

Test cases

Truthy:

[[1, 2, 3, 3, 4], [1, 2, 3, 4, 5], [3, 5, 6, 1, 4], [1, 5, 3, 4, 6], [4, 5, 2, 3, 5], [1, 4, 3, 2, 2], [5, 4, 3, 6, 3], [5, 3, 5, 4, 6], [2, 4, 5, 1, 3], [3, 6, 4, 5, 3], [5, 6, 4, 3, 5], [4, 5, 3, 6, 3], [4, 5, 5, 3, 2], [4, 5, 2, 3, 5], [4, 6, 5, 3, 6], [4, 2, 3, 1, 5], [3, 6, 4, 6, 5], [5, 2, 1, 3, 4], [4, 4, 1, 2, 3], [4, 1, 4, 2, 3], [5, 1, 4, 3, 6], [5, 2, 2, 3, 4], [4, 4, 6, 5, 3], [2, 4, 3, 5, 1], [5, 4, 2, 5, 3], [2, 3, 5, 5, 4], [1, 6, 3, 4, 5], [4, 5, 3, 3, 6], [6, 4, 3, 6, 5], [4, 6, 6, 5, 3], [4, 3, 5, 2, 2], [2, 3, 2, 1, 4], [4, 2, 6, 1, 3], [4, 4, 5, 3, 6], [4, 5, 6, 3, 6]]


Falsy:

[[1, 2, 3, 5, 6], [5, 1, 1, 6, 6], [4, 6, 4, 1, 1], [6, 4, 1, 6, 4], [4, 6, 3, 6, 6], [2, 1, 4, 6, 4], [2, 6, 1, 5, 6], [2, 6, 1, 5, 6], [3, 6, 5, 3, 2], [3, 2, 3, 5, 3], [5, 5, 6, 2, 3], [3, 4, 6, 4, 3], [1, 4, 5, 5, 1], [1, 4, 4, 4, 1], [1, 6, 5, 1, 4], [6, 6, 4, 5, 4], [5, 3, 3, 3, 2], [5, 2, 1, 5, 3], [3, 5, 1, 6, 2], [6, 4, 2, 1, 2], [1, 3, 1, 3, 2], [3, 1, 3, 4, 3], [4, 3, 1, 6, 3], [4, 6, 3, 3, 6], [3, 6, 3, 6, 4], [1, 1, 3, 1, 3], [5, 5, 1, 3, 2], [3, 4, 2, 6, 6], [5, 4, 2, 6, 1], [2, 4, 4, 5, 4], [3, 6, 2, 5, 5], [2, 5, 3, 5, 1], [3, 2, 2, 3, 4], [5, 2, 2, 6, 2], [5, 6, 2, 5, 6]]


Inspired by this

Catalogue

var QUESTION_ID=74997;var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe";var COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk";var OVERRIDE_USER=30525;var answers=[],answers_hash,answer_ids,answer_page=1,more_answers=true,comment_page;function answersUrl(index){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+index+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(index,answers){return"http://api.stackexchange.com/2.2/answers/"+answers.join(';')+"/comments?page="+index+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:true,success:function(data){answers.push.apply(answers,data.items);answers_hash=[];answer_ids=[];data.items.forEach(function(a){a.comments=[];var id=+a.share_link.match(/\d+/);answer_ids.push(id);answers_hash[id]=a});if(!data.has_more)more_answers=false;comment_page=1;getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:true,success:function(data){data.items.forEach(function(c){if(c.owner.user_id===OVERRIDE_USER)answers_hash[c.post_id].comments.push(c)});if(data.has_more)getComments();else if(more_answers)getAnswers();else process()}})}getAnswers();var SCORE_REG=/<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/;var OVERRIDE_REG=/^Override\s*header:\s*/i;function getAuthorName(a){return a.owner.display_name}function process(){var valid=[];answers.forEach(function(a){var body=a.body;a.comments.forEach(function(c){if(OVERRIDE_REG.test(c.body))body='<h1>'+c.body.replace(OVERRIDE_REG,'')+'</h1>'});var match=body.match(SCORE_REG);if(match)valid.push({user:getAuthorName(a),size:+match[2],language:match[1],link:a.share_link,});else console.log(body)});valid.sort(function(a,b){var aB=a.size,bB=b.size;return aB-bB});var languages={};var place=1;var lastSize=null;var lastPlace=1;valid.forEach(function(a){if(a.size!=lastSize)lastPlace=place;lastSize=a.size;++place;var answer=jQuery("#answer-template").html();answer=answer.replace("{{PLACE}}",lastPlace+".").replace("{{NAME}}",a.user).replace("{{LANGUAGE}}",a.language).replace("{{SIZE}}",a.size).replace("{{LINK}}",a.link);answer=jQuery(answer);jQuery("#answers").append(answer);var lang=a.language;lang=jQuery('<a>'+lang+'</a>').text();languages[lang]=languages[lang]||{lang:a.language,lang_raw:lang.toLowerCase(),user:a.user,size:a.size,link:a.link}});var langs=[];for(var lang in languages)if(languages.hasOwnProperty(lang))langs.push(languages[lang]);langs.sort(function(a,b){if(a.lang_raw>b.lang_raw)return 1;if(a.lang_raw<b.lang_raw)return-1;return 0});for(var i=0;i<langs.length;++i){var language=jQuery("#language-template").html();var lang=langs[i];language=language.replace("{{LANGUAGE}}",lang.lang).replace("{{NAME}}",lang.user).replace("{{SIZE}}",lang.size).replace("{{LINK}}",lang.link);language=jQuery(language);jQuery("#languages").append(language)}}
body{text-align:left!important}#answer-list{padding:10px;width:290px;float:left}#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="language-list"> <h2>Shortest Solution 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> <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> <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>

• Does the truthy value have to be consistent? Could I output some (non-constant) positive integer for truthy results and 0 for falsy results? Mar 7 '16 at 18:13
• @MartinBüttner It needn't be consistent. Mar 7 '16 at 18:13
• Make sure to check if it works on [1,2,3,3,4]. Many answers die because of this. Mar 7 '16 at 18:30
• Can I assume the array is padded by zeros? Mar 7 '16 at 18:58
• @CatsAreFluffy "die" Mar 7 '16 at 20:19

Minkolang, 24 bytes

Try it here! Saved 3 bytes thanks to El'endia Starman!

$ns4[2~c-1R]xs011130$ZN.
$n C take all input C s C sort the stack C 4[ ] C repeat that 4x C 2~c C copy 2nd elem. C - C subtract top 2 C 1R C rot stack once C x C drop top elem. C s C sort the stack C 011130$Z      C count # of 111 C
N.    C output and end C


I'm pretty happy that the explanation came in fixed width. It was coincidental the first three times, then I decided to roll with it.

JavaScript (ES6), 57 bytes

s=>(g=(n,m=4)=>!m||~s.search(n)&&g(n+1,m-1))(1)|g(2)|g(3)


Takes a string as input, and checks whether it contains all the digits in the sets (1,2,3,4), (2,3,4,5) or (3,4,5,6). Returns 1 if it does and 0 if it doesn't.

PowerShell v3+, 8545 42 bytes

-join($args|sort -u)-match"1234|2345|3456"  Requires v3 or newer for the -Unique flag on the Sort-Object cmdlet. Takes commandline $args as individual elements, then sorts only the -unique values, and -joins it together into a string. If that then regex -matches against the 1234|2345|3456 pattern, output is True; else, False. The Boolean value is left on the pipeline and output is implicit.

Saved 3 bytes thanks to Titus

C++17, 6968 66 bytes

-1 byte for changing truthy and falsey value. -2 bytes for setting r inside the initializer list.

As unnamed generic variadic lambda returning via reference parameter.

[](int&r,auto...p){for(int s:{r=30,60,120})r*=s-(s&(...|(1<<p)));}


The folding expression (...|(1<<p)) shifts 1 to the left for each item in p and bitwise-or them to create a set of fallen dice. Then compare this with the bitsets of small straights. Returns 0 for detection and otherwise for otherwise.

Ungolfed and usage:

#include<iostream>
using namespace std;

auto f=
[](int& r, auto...p){
for (int s: {r = 30,60,120})
r *= s - (s & (...|(1<<p)));
}
;

int main() {
int r;
f(r,1,2,3,4,6);
cout << r << endl;
f(r,2,3,4,5,2);
cout << r << endl;
f(r,3,4,5,6,6);
cout << r << endl;
f(r,1,1,3,5,4);
cout << r << endl;
}


It seems like GCC 6.2 has a bug, that only left folds of the form ...|f(p) work, right folds like f(p)|... don't work.

previous 77 76 bytes solution

-1 byte for 0-indexing. Another -1 for changing truthy/falsey value.

Heavy use of parameter packs and fold expressions. As unnamed lambda returning via reference parameter:

[](int&r,auto...p){[&](auto...s){r=(...*(s-(s&(...|(1<<p)))));}(30,60,120);}


Ungolfed & explanation:

[](int& r, auto... p){ //out-reference and parameter pack
[&](auto...s){       //declare inline lambda accepting another parameter pack and reference capture everything
r=(...*(s-(s&(...|(1<<p-1)))));    //explained below
}(30,60,120);         //immediately call the lambda with the bitset of small streets
}

r=(...*(s-(s&(...|(1<<p)))));
(1<<p)      shift 1 to the left for each input p
(...|(1<<p))     fold with binary-or, create bitset
s&(...|(1<<p))     binary-and with each constant s
s-(s&(...|(1<<p)))    subtracts any of the constants, so if any small street is present the result is 0
...*(s-(s&(...|(1<<p))))   fold with multiply, like any(...)
r=(...*(s-(s&(...|(1<<p))))); apply result to output parameter


Usage:

int main() {
int r;
f(r,1,2,3,5,4);
cout << r << endl;
}


Clojure, 54 bytes

#(some(fn[p](every?(set %)p))(partition 4 1(range 7)))


(partition 4 1(range 7)) returns possible small-straights (we can ignore 0): ((0 1 2 3) (1 2 3 4) (2 3 4 5) (3 4 5 6))

some returns true if some arguments of p return true from the anonymous function and nil otherwise. Within every? returns true if the "function" (set of values of input list) returns a truthy value for all arguments of p. Thus the combination of some and every? checks if there exists such partition for which all numbers are found from the input argument %.

awk, 51 chars

{for(;++i<7;)a=a gsub(i,1);exit sub(/[12]{4}/,1,a)}


Solution searches for numbers [1-6] from given string in that order, packs their frequencies to var a (11235 -> 21101), searches for four consecutive 1s or 2s (there can be only 1 2) and exits with search result.

$echo 1,2,2,3,5|awk '{for(;++i<7;)a=a gsub(i,1);exit sub(/[12]{4}/,1,a)}'$ echo $? 0$ echo 1,2,3,4,5|awk '{for(;++i<7;)a=a gsub(i,1);exit sub(/[12]{4}/,1,a)}'
$echo$?
1


btw, small straight over here is 1,2,3,4,5.

Perl 6, 3224 18 bytes

{@_⊇^4+1+any ^3}


^4 is the range of integers from 0 to 3. ^4+1 is the range of integers from 1 to 4. any ^3 is the or-junction of the values 0, 1, and 2. Adding it to the range 1-4 produces the or-junction of the ranges 1-4, 2-5, and 3-6. A truthy value is returned if the argument list @_ is a superset of any of those ranges.

Kotlin, 80 68 bytes

d.toHashSet().zipWithNext{a,b->b-a}.joinToString("").contains("111")


Try it online!

Brachylog, 5 bytes

pb~⟦₂


Try it online!

Takes input through the input variable and outputs through success or failure. (The output variable contains the smallest and largest numbers within the small straight.)

p        Some permutation of the input,
b       with its first element removed,
~⟦₂    is a range.


This brute forces permutations until either one of them is of the form [_ | sorted small straight] or there aren't any permutations left to check.

05AB1E, 6 bytes

ê¥JƵAå


Try it online!

ê          # sorted uniquified input
¥         # deltas (e.g. [1, 2, 3, 4, 6] => [1, 1, 1, 2])
J        # join
ƵA      # compressed integer 111
å     # is it a substring of the joined deltas?

• Lol. After seeing the MATL answer just yet I was about to post this exact solution, haha. Here a test suite. Alternative 6-byter: ê¥¥¥0å. Apr 24 '20 at 10:34

K (ngn/k), 16 bytes

{|/&/~^x?x+/:!4}


Try it online!

Another port of @ngn's APL answer, using ~^x?y as the membership test.

R + pryr, 49 bytes

(pryr is a package that gives shorthand for creating functions)

f=pryr::f;f(any(combn(x,4,f(sum(diff(x))))==3))


Using @ETHproductions idea of taking all combinations of length 4

• I've edited your post to include the library name; including it is standard procedure for nonstandard libraries. Mar 7 '16 at 23:28

C++, 189 168 144 143 bytes

void main(int i,char *x[]){char c[6]={0},*v=x[1];for(i=0;v[i];)c[v[i++]-'1']++;printf(c[2]&&c[3]&&(c[0]&&c[1]||c[1]&&c[4]||c[4]&&c[5])?"T":"F")}


Input must be a 5-digit integer on the command line, e.g. test.exe 12334

• Lots of extra stuff here you don't need. That whole if statement for error-checking isn't necessary, as we can assume the input is valid. Also, you only need to output a "truthy" or "falsey" value, you don't need to literally print the words "TRUE" or "FALSE". A simple 1 or 0 would suffice. The return statement is also not needed. Mar 8 '16 at 15:48
• The testcases as given include square brackets, spaces and commas, which would cause UB at c[v[i]-'1']++. Thought I might get flamed for voiding main. Still, I could still shave 21 bytes off with the other changes. Mar 8 '16 at 20:14
• The exact input format on PPCG is usually left up to the programmers. Certainly many (probably most) of the answers on here would fail if they were required to use the input format exactly as given. Oh, and nobody is expecting code-golf to be 100% standards-compliant. If it compiles and produces correct output, it's good enough. Mar 8 '16 at 20:20
• "F" can be replaced with "" Mar 9 '16 at 1:16

PHP, 65 bytes

while($i++<3)array_intersect($r=range($i,$i+3),$argv)<$r?:die(1);


Takes dice from command line arguments; exits with code 1 for straight, 0 else. Run with -r.

Stax, 7 bytes

Ç╒t╛♦─├


Run and debug it

Algorithm:

• Sort
• Remove duplicates
• Compute pairwise differences
• Check for [1,1,1] as contiguous sub-array.

J, 18 bytes

4 e.1#.]e.~]+/i.@4
`

Try it online!