14
\$\begingroup\$

Keep Talking and Nobody Explodes is a local multiplayer game where one player has control over a virtual "bomb", and has to be guided by another player, the "expert", who has access to a bomb defusal manual. One of the modules to be disarmed in the game is the keypad module, which is what we'll be dealing with in this challenge.

The task

Input will start with a single line of printable ASCII characters except space (0x21 to 0x7E). These represent the keypad buttons visible to you.

The next few lines will represent "keys" – only one line will contain all of the characters of the first line, not necessarily in order. Your task is to output the keypad characters, in the order of the matching key line.

For example, if the input was

5~Fy
HrD7K!#}
Ui%^fHnF
)Tf;y~I5
~Fi(&5gy
,'Xd#5fZ

then the keypad buttons are 5, ~, F and y. Only the 4th key line ~Fi(&5gy contains all of these characters, so we output the keypad characters in the order in which they appear, i.e. ~F5y.

Rules and clarifications

  • Input must be a single multiline string, with the keypad buttons and key lines on separate lines.
  • There will be exactly one key line which contains all of the keypad characters.
  • Every line, i.e. the initial keypad line and following key lines, will have no duplicate characters.
  • Unlike the game, you may not assume anything about the number of keypad characters, the length of each key line or the number of key lines. However, all key lines are guaranteed to be the same length.
  • The output may contain a single optional trailing newline. Similarly you may assume either way about an optional trailing newline in the input, but please specify in your answer if you need the assumption.
  • Although this already seems to be common practice, I'll state explicitly: terminating with an error is okay for this challenge, as long as STDOUT output is correct (if this is your chosen form of output). Hopefully this will make handling input easier.

Test cases

7
4?j01C3"ch
KP.OG>QB)[
z#)Kn"I2&.
]#,D|sBFy5
Qzj*+~7DLP

Output: 7. Only the last line contains a 7.

0b~
Ob+hy{M|?;>=dtszPAR5
*8rCfsw|3O9.7Yv^x>Hq
$ip.V@n}|La:TbIt^AOF
jZ[Ec4s0|%b*$id',~J6
z*#b}-x$Ua&!O2;['T+?
NVj_X8rlhxfnS\.z}];c
bykscf.w^dnWj+}-*2g_
VP`AJH|&j5Yqmw/"9IMc

Output: 0b~. The 4th key line already contains the characters in the right order.

MTuz
bIAr>1ZUK`s9c[tyO]~W
oMGIi/H&V"BeNLua%El=
j*uYbplT:~);BM|_mPZt
Q}z5TC@=6pgr<[&uJnM%
YOA(F~_nH6T{%B7[\u#5
y&t"8zQn{wo5[Idu4g:?
[0tZG"-fm!]/|nqk,_2h
dA&C.+(byo6{7,?I}D@w

Output: zTuM. The key line is the 4th one, although the 3rd key line is a close miss.

o@nj<G1
f]?-<I6h2vS*%l=:}c8>LK5rMdyeon,;sE[@m(73
ibhp+2Hq6yKzIf_Zo}EO3-[*0/e&Fvd]wQU=|%`C
;}>d'cg~CPtQG&%L\)MUl419bkTZ7@]:[*H"RyYj
L^<:zXJ#kj$EFlwN%B`Dd,Cs?]xRZ*K9-uQ.@&f+
i1v'7:90R-l}FMxj`,DTWK+(n32Z4Vs[p@%*eS!d
B|^Ti/ZG$}ufL9*wE[AVt]P7CrX-)2JpD<sYxd6O
ex.$4#KarS^j+'_!B"]H[\83:(DCXUgI*Lct?qAR
^GXQoy*KW&v}n']Em~\N9)fxP(qC=7#4sRdcD6%5
;inr[&$1j_!F~@pzo#blv]}<'|fRds6OW%tEg"G2
e;0T#gfo^+!:xHDN&4V=In?AwhEv$2Fd~ZLz_\81

Output: n1j@o<G. The key line is the second last line.

Scoring

This is , so the code in the fewest bytes wins.

\$\endgroup\$
3
  • \$\begingroup\$ Is STDOUT the only acceptable output method, or is a function return value also allowed? \$\endgroup\$
    – Zgarb
    Commented Nov 11, 2015 at 18:36
  • \$\begingroup\$ @Zgarb Function input and output are both okay \$\endgroup\$
    – Sp3000
    Commented Nov 11, 2015 at 20:15
  • \$\begingroup\$ sigh I have a solution that works for one test case...too many escape characters in the other test cases. Oh well. \$\endgroup\$
    – Kyle Kanos
    Commented Nov 13, 2015 at 0:46

12 Answers 12

10
\$\begingroup\$

CJam, 13 12 bytes

qN/(f&{,}$W=

Test it here.

Explanation

q     e# Read all input.
N/    e# Split into lines.
(     e# Pull off the keypad buttons.
f&    e# Take the set intersection of each key line with the keypad, preserving the order
      e# order in the key line.
{,}$  e# Sort the results by length.
W=    e# Pick the last (longest) one.
\$\endgroup\$
8
\$\begingroup\$

Pyth, 10

@zhf!-zT.z

Try it online

Explanation

@zhf!-zT.z         ##  z = first line of input, .z = list of rest of lines
   f    .z         ##  Filter .z as T based on
    !-zT           ##  Whether removing all the letters from z that appear in T leaves an
                   ##  Empty string or not (keep the ones that give empty strings)
  h                ##  Take the first such line (necessary indexing, shouldn't ever matter)
@z                 ##  @ is setwise intersection. Pyth implements this by iterating over
                   ##  each element of the second argument and keeping values that appear
                   ##  in the first argument, which gives the intended result
\$\endgroup\$
7
\$\begingroup\$

Pyth, 9 bytes

[email protected]

Demonstration

@Lz.z: Filter all lines for the intersection with the first line.

olN: Order by length

e: Take the longest.

\$\endgroup\$
3
\$\begingroup\$

Haskell, 49 bytes

g(!)(a:b)=[c|d<-b,all(!d)a,c<-d,c!a]
g elem.lines

The first line defines a helper function g, the unnamed function on the second line is my answer.

Explanation

The algorithm is the obvious one: split input into lines, find the line that contains all characters of the first line, and filter out all the other characters on that line.

g(!)(a:b)=                            -- g gets a binary function ! and list of strings a:b
          [c|                         -- and returns the string of characters c where
             d<-b,all(!d)a,           -- d is drawn from b and x!d holds for all x in a,
                           c<-d,c!a]  -- and c is drawn from d and c!a holds.
g elem.lines                          -- The input is split into lines and fed to g elem;
                                      -- then x!d means x `elem` d in the above.
\$\endgroup\$
3
\$\begingroup\$

Prolog, 204 190 bytes

This could have been a nice challenge for Prolog if it hadn't been for the combined requirements of multiline input and unescaped characters ' and " in the input. A big chunk of the code (p and r) exist to read a file as character codes which is what I had to do to take unescaped input on several lines.

If only ' existed as an unescaped character, I could read input as a string.
If only " existed as an unescaped character, I could read input as an atom.
If input wasn't multiline, say separated by space instead, I could read it as one line to codes.

r(I,[H|T]):-read_line_to_codes(I,H),H\=end_of_file,r(I,T).
r(_,[]).
q(_,[]).
q(E,[H|T]):-subset(E,H),intersection(H,E,X),writef("%s",[X]);q(E,T).
p:-open("t",read,I),r(I,[H|T]),q(H,T),!.

How it functions

  1. Opens file t (which contains all input) to read
  2. Read all lines as character codes and place in a list of lists (1 list per row)
  3. Recurses over tail lists and checks if head list exist as a subset of that list
  4. Intersects matched list with head to get wanted characters in the correct order
  5. Prints solution

How to run
Program is run with command:
p.
File namned t containing input has to be in the same directory.

Edit: Saved 14 bytes by unifying 2 q-clauses with OR.

\$\endgroup\$
2
\$\begingroup\$

MATLAB, 107 bytes

b=char(strsplit(char(inputdlg),' '));[~,x]=ismember(b,b(1,:));[~,f]=min(abs(1./sum(~x')-1));b(f,(~~x(f,:)))

This ended up being a very sloppy piece of code...

When being run, an input dialog is opened where a multi-line string can be pasted into (newlines are converted to spaces and the output will be a cell with 1 very long string). I chose to convert the resulting cell to a char which makes it possible to split at the spaces (result is a cell array) and then again convert to char to retrieve the intended shape. MATLAB's built-in ismember function does a good job here in comparing our first line to the other lines.

After that it gets nasty... I've tried a lot of ways to exclude the first line from my 'best match' check and ended up with this. We look for the line and then use this information to grab the indices (by converting our ismember output to logicals) that we want our output characters from.

\$\endgroup\$
2
\$\begingroup\$

Wolfram Language 106 bytes

c=Characters[InputString[]~StringSplit~"\n"];o=c[[1]];t=Select;t[t[Rest@c,#~SubsetQ~o&][[1]],o~MemberQ~#&]

Example input:

input popup

Output:

output result

Explanation of the code : First with InputString we get the full string of input, then we get the first set of letters by splitting the string by newline, and saving all the characters of the first one in variable o. Next, we select from the rest of the input lines those lines that have the first line's characters (saved as variable o) as a subset. Then with that line selected, we grab the members of that line that are in the original set.

Edit : Thanks to Martin Büttner for the tips on using infix notation and my unnecessary variables

\$\endgroup\$
4
  • \$\begingroup\$ Yay, Mathematica. Some golfing hints: As far as I can tell you use c and i only once, so there's no benefit in assigning them to variables. You can probably save some bytes from this tip. By not giving o a name. s[[1]] is #&@@s (same for your second use of [[1]]). You can use StringSplit without the second parameter (because it splits on whitespace by default). SubsetQ and MemberQ can use infix notation to save a byte, e.g. #~SubsetQ~o. \$\endgroup\$ Commented Nov 12, 2015 at 16:45
  • \$\begingroup\$ I changed it some, and didn't notice as I changed it that I only used i and c once, thanks for the tip! Also, I need to have the second parameter to StringSplit, as there were some weirdness going on with some of the characters getting interpreted as whitespace (that aren't really whitespace) \$\endgroup\$ Commented Nov 12, 2015 at 16:51
  • \$\begingroup\$ Interesting. In that case you can still embed a literal newline instead of writing \n, to save one byte, and use infix notation to save another. \$\endgroup\$ Commented Nov 12, 2015 at 16:54
  • \$\begingroup\$ Yeah, not entirely sure what's going on with StringSplit in that case, it might actually be a consequence of using InputString \$\endgroup\$ Commented Nov 12, 2015 at 17:10
2
\$\begingroup\$

Japt -h, 6 8 bytes

Takes the keypad as an array of lines. The buttons can be either a string or a character array. +2 bytes to accommodate the cumbersome input format.

·ËoFgÃñl

Try it

·ËoFgÃñl     :Implicit input of multi-line string
·            :Split on newlines
 Ë           :Map each element in array F
  o          :  Remove characters not contained in
   Fg        :  First element of F
     Ã       :End map
      ñ      :Sort by
       l     :  Length
             :Implicit output of last element
\$\endgroup\$
3
  • \$\begingroup\$ "Input must be a single multiline string, with the keypad buttons and key lines on separate lines." \$\endgroup\$ Commented Nov 3, 2022 at 17:33
  • 1
    \$\begingroup\$ Thanks, @DominicvanEssen. I should really learn to check the dates on challenges and read through the specs for unnecessary restrictions, rather than assuming current standards. Will update in the morning; think it'll cost me 3 bytes. \$\endgroup\$
    – Shaggy
    Commented Nov 3, 2022 at 20:56
  • 1
    \$\begingroup\$ Nice. I kind-of usually agree re: cumbersome input format, but in this case I found that it was actually an interesting restriction to an otherwise pretty easy challenge. Probably depends on what's on offer in each language, though... \$\endgroup\$ Commented Nov 4, 2022 at 11:50
1
\$\begingroup\$

Python 2, 112 bytes

import sys
i=sys.stdin.readlines()
print[''.join(c for c in l if c in i[0])for l in i[1:]if set(i[0])<set(l)][0]

Example run: Ideone

\$\endgroup\$
1
\$\begingroup\$

Javascript (ES6), 107 104 102 bytes

Snippet demo for supporting browsers.

f=x=>([a]=x.split`
`).map(y=>[...y].filter(z=>~a.indexOf(z)-x).join(x='')).find(z=>z.length==a.length)
<textarea id="i" rows="6" cols="45">o@nj<G1
f]?-<I6h2vS*%l=:}c8>LK5rMdyeon,;sE[@m(73
ibhp+2Hq6yKzIf_Zo}EO3-[*0/e&Fvd]wQU=|%`C
;}>d'cg~CPtQG&%L\)MUl419bkTZ7@]:[*H"RyYj
L^<:zXJ#kj$EFlwN%B`Dd,Cs?]xRZ*K9-uQ.@&f+
i1v'7:90R-l}FMxj`,DTWK+(n32Z4Vs[p@%*eS!d
B|^Ti/ZG$}ufL9*wE[AVt]P7CrX-)2JpD<sYxd6O
ex.$4#KarS^j+'_!B"]H[\83:(DCXUgI*Lct?qAR
^GXQoy*KW&v}n']Em~\N9)fxP(qC=7#4sRdcD6%5
;inr[&$1j_!F~@pzo#blv]}<'|fRds6OW%tEg"G2
e;0T#gfo^+!:xHDN&4V=In?AwhEv$2Fd~ZLz_\81</textarea><br /><input type="button" onclick="o.value=f(i.value)" value="Run"> Output: <input type="text" id="o" readonly />

Commented:

f=x=>
([a]=x.split('\n')) // split input by newlines, assign first value to a
.map(y=> // map function to each line
    [...y].filter(z=> // filter characters
        ~a.indexOf(z)-x // a has character z and not the first item (x is still set)
    ).join(x='') // join characters with empty string, reset x flag
).find(z=>z.length==a.length) // return string with same length as a
\$\endgroup\$
1
\$\begingroup\$

Ruby, 96 71 63 bytes

-8 bytes thanks to south

Inspired by Shaggy’s Japt solution. Requires a trailing newline in the input.

->s{a,b=s.split$/,2
b.gsub(/./){a[$&]&&$&}.lines.max_by &:size}

Attempt This Online!

\$\endgroup\$
2
  • \$\begingroup\$ Nice, I managed 63. String#[] helps a lot :P \$\endgroup\$
    – south
    Commented Nov 3, 2022 at 18:34
  • \$\begingroup\$ @south Nice work, thanks! \$\endgroup\$
    – Jordan
    Commented Nov 3, 2022 at 18:36
0
\$\begingroup\$

Nibbles, 8 7 bytes (14 nibbles)

/\+=~.;@`&$;$,
      .         # map over
       ;@       # all lines of input
         `&     # getting list intersection 
           $    # of each line 
            ;$  # with the first line of input;
    =~          # now group intersections by 
              , # length,
   +            # flatten the list-of-groups,
  \             # reverse it 
                # (so the 'found' keys are now first,
                # and the input is second),
/               # finally, fold from right
                # returning the (implicit) right argument
                # at each step
                # (so returning the first element)

enter image description here

\$\endgroup\$

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.