What numbers are these?

22

2

While I was writing numbers I noticed after a while that my keyboard had the Shift key pressed and blocked and all I wrote was $%&-like characters. And even worse, I had been switching between the English and Spanish keyboard layouts so I don't know which one I used for each number.

Challenge

Given a string containing symbol characters, try to guess which number I wrote. My keyboard produces the following characters for the numbers when the Shift is pressed:

1234567890
----------
!"·$%&/()=  Spanish layout
!@#$%^&*()  English layout
  • The input will be a non-null, non-empty string composed of the symbols above.
  • The output will be a single number if the keyboard layout can be inferred from the string (i.e. if the string contains a @ an English layout was used, and if the string contains a " a Spanish layout was used) or if the number is the same for both layouts (i.e. the input is !$ which translates as 14 for both layouts); otherwise the output will be the two possible numbers for both layouts if it cannot be inferred and the resulting numbers are different.
  • The input string will always be written in a single layout. So you don't need to expect "@ as input.

Examples

Input  -->  Output
------------------
/()         789        (Spanish layout detected by the use of /)
$%&         456,457    (Layout cannot be inferred)
!@#         123        (English layout detected by the use of @ and #)
()&!        8961,9071  (Layout cannot be inferred)
((·))       88399      (Spanish layout detected by the use of ·)
!$          14         (Layout cannot be inferred but the result is the same for both)
!!$$%%      114455     (Layout cannot be inferred but the result is the same for both)
==$"        0042/42    (Spanish layout, if a number starts with 0 you can choose to
                       omit them in the result or not)

Single character translations:
------------------------------
!   1    
"   2
·   3
$   4
%   5
&   6,7
/   7
(   8,9
)   9,0
=   0
@   2
#   3
^   6
*   8

This is , so may the shortest code for each language win!

Charlie

Posted 2018-10-12T20:34:41.550

Reputation: 11 448

Dang it, that · is challenging... – Erik the Outgolfer – 2018-10-12T20:39:03.023

2@EriktheOutgolfer in fact the · is useless for Spanish, it is only used in the Catalan language. – Charlie – 2018-10-12T20:41:04.550

Is output like {(8, 9, 6, 1), (9, 0, 7, 1)} (for the 4th test case) acceptable? – Lynn – 2018-10-12T21:01:55.993

@Lynn yes, it is. – Charlie – 2018-10-12T21:03:31.747

When outputting 2 numbers, does the order matter? – Shaggy – 2018-10-12T22:30:47.133

@Shaggy not at all. :-) – Charlie – 2018-10-12T22:35:49.180

For my English layout it's !"£$%^&*() – Okx – 2018-10-13T08:33:54.777

@Okx interesting, I suppose my sysadmin has installed the American English layout in my computer. The British one seems to be a mix of both. – Charlie – 2018-10-13T08:40:52.327

Is that dot (·) the same as byte 250 in CP437 and DOS Latin-1? If so, may we encode input and our submissions in such code pages able to represent all of the characters required in a single byte? – Οurous – 2018-10-14T05:24:54.950

@Οurous the byte 250 in CP437 seems to be a punctuation mark resembling the middle dot, so yes, you can use it. I'm not sure what you want to do with the code pages but please, go ahead. – Charlie – 2018-10-14T05:43:51.500

@Charlie The reason I ask is it saves a lot of space not having to handle an otherwise multi-byte character. – Οurous – 2018-10-14T22:11:52.323

Can the input be taken as a list of characters instead of string? – Kevin Cruijssen – 2018-10-15T06:48:42.590

@KevinCruijssen yes, of course. – Charlie – 2018-10-15T07:00:15.807

Answers

6

Jelly, 32 31 bytes

O“=!"Ṣ$%&/()“)!@#$%^&*(‘iⱮ€PƇ’Q

Try it online!

  • -1 bytes thanks to Erik the Outgolfer

O“!"Ṣ$%&/()=“!@#$%^&*()‘iⱮ€PƇ%⁵Q
O                                  ord of each character in the input
 “!"Ṣ$%&/()=“!@#$%^&*()‘           Constant that yields the list:
                                      [[33, 34, 183, 36, 37, 38, 47, 40, 41, 61],
                                       [33, 64, 35, 36, 37, 94, 38, 42, 40, 41]
                          €        For each list of numbers:
                         Ɱ           For each ord of the characters in the input:
                        i              Find the index of the ord of the character
                                       in the list of numbers.
                                       If the number is not found, `i` returns zero
                                       which means it's a character from only one
                                       keyboard.
                                   There are now two lists of numbers 1-10.
                            Ƈ      Keep the list(s) that: 
                           P         have nonzero product.
                             %⁵    Modulo 10. This maps 10->0.
                               Q   Unique elements. This removes duplicates if the two numbers are the same.

dylnan

Posted 2018-10-12T20:34:41.550

Reputation: 4 993

131 bytes. – Erik the Outgolfer – 2018-10-13T11:33:46.210

4

Python 3, 76 bytes

lambda s:{(*map(k.find,s),)for k in['=!"·$%&/()',')!@#$%^&*(']if{*s}<={*k}}

Try it online!

Lynn

Posted 2018-10-12T20:34:41.550

Reputation: 55 648

4

Perl 6, 62 bytes

{set grep {!/\D/},TR'=!"·$%&/()'0..9',TR')!@\x23$%^&*('0..9'}

Try it online!

Returns a Set. (Could be made two or three bytes shorter if there wasn't a bug in Rakudo's handling of # in search lists.)

nwellnhof

Posted 2018-10-12T20:34:41.550

Reputation: 10 037

3

Java (JDK), 173 bytes

Golfed

c->{String s="",e=s;var m="21#3457#908###6##12#456389###0#7".split("");for(int l:c){e+=m[l=l%16];s+=m[l+16];}return s.equals(e)|s.contains("#")?e:e.contains("#")?s:s+","+e;}

Try it online!


Ungolfed
c->{                                                      // Lamdba taking char array as input
    String s="",e=s;                                      // Initialise Spanish and English strings
    var m="21#3457#908###6##12#456389###0#7".split("");   // Create magic hashing lookup array (see below)
    for(int l:c){                                         // Loops through all chars in input
        e+=m[l=l%16];                                     // Get english number from array and append
        s+=m[l+16];                                       // Get Spanish number from array and append
    }
    return s.equals(e)|s.contains("#")?e:                 // If equal or Spanish is invalid return english
        e.contains("#")?s:                                // If English is invalid return Spanish
        s+","+e;                                          // If both are valid but not equal, return both
}


The Magic Hashing Lookup Array

After some experimenting with values I realised that each of the ASCII values of the characters !"·$%&/()=@#^* modulo 16 returns a unique number. The 'magic hashing lookup array' stores the English numbers associated with each character at this unique index, and each of the Spanish numbers at this index offset by 16, making fetching the required number from the array trivial for each language. A hash is stored for values that are invalid for either language.

Luke Stevens

Posted 2018-10-12T20:34:41.550

Reputation: 979

I don't suppose you could use toCharArray() and the int values to make this shorter? (Just an idea, I haven't tried it yet.) – Quintec – 2018-10-13T00:46:59.023

@Quintec I tried it, but the extra bytes from toCharArray() and calculating the exponent to be applied to the int value made it far longer than both the .contains() statements. – Luke Stevens – 2018-10-13T16:35:54.577

s.equals(e)|s.contains("#") can be s.matches(e+"|.*#.*"). – Kevin Cruijssen – 2018-10-15T06:41:36.073

Suggest l%=16 instead of l=l%16 – ceilingcat – 2020-01-03T01:26:27.663

3

Japt, 38 bytes

Outputs an array of strings with the Spanish layout first.

"=!\"·$%&/())!@#$%^&*("òA £ËXbD
kø'- â

Try it

Shaggy

Posted 2018-10-12T20:34:41.550

Reputation: 24 623

2

Jelly, 38 bytes

183Ọ“=!"“$%&/()”j,“)!@#$%^&*(”iⱮ€⁸ẠƇ’Q

Try it online!

Erik the Outgolfer

Posted 2018-10-12T20:34:41.550

Reputation: 38 134

Nice! Just one question, I have tried your code with () or (()) as input, but your code then return nothing. I suppose that's a limitation with what Jelly receives as input? – Charlie – 2018-10-12T21:27:56.843

1@Charlie Try with '()' and '(())' respectively. Yes, if you don't quote the argument, it's only inputted as a string if it can't be evaluated to a Python 3 value. – Erik the Outgolfer – 2018-10-12T21:28:23.737

2

Retina 0.8.2, 60 bytes

.+
$&¶$&
T`=!"·$%&/()`d`^.+
T`)!@#$%^&*(`d`.+$
D`
Gm`^\d+$

Try it online! Link includes test cases. Explanation:

.+
$&¶$&

Duplicate the input.

T`=!"·$%&/()`d`^.+
T`)!@#$%^&*(`d`.+$

Try to translate each line according to a different keyboard layout.

D`

Deduplicate the result.

Gm`^\d+$

Only keep lines that only contain digits.

Neil

Posted 2018-10-12T20:34:41.550

Reputation: 95 035

Do you need the m in your last stage? – ovs – 2018-10-13T10:38:41.607

@ovs Yes, the matches run first, and then the lines are split and lines containing matches are kept. – Neil – 2018-10-13T12:12:57.873

1

JavaScript (ES6), 99 bytes

s=>(g=l=>a=s.replace(/./g,c=>l.indexOf(c)))('=!"·$%&/()',b=g(')!@#$%^&*('))>=0?a-b&&b>=0?[a,b]:a:b

Try it online!

How?

The helper function \$g\$ attempts to convert the input string using a given layout.

Invalid characters are replaced with \$-1\$, which results in either a valid but negative looking numeric string (if only the first character was missing), or an invalid numeric string. Either way, the test x >= 0 is falsy.

Arnauld

Posted 2018-10-12T20:34:41.550

Reputation: 111 334

1

Clean, 116 bytes

import StdEnv,Text
$s=removeDup[foldl(<+)""d\\r<-["=!\"·$%&/()",")!@#$%^&*("],d<-[[indexOf{c}r\\c<-s]]|all((<) -1)d]

Try it online!

Takes input and is encoded in CP437. TIO only supports UTF-8, so an escape is used in the demo code to get the literal byte value 250 corresponding to the centre dot (counted as one byte).

Οurous

Posted 2018-10-12T20:34:41.550

Reputation: 7 916

The !$% input should output only one number, not two, as the result is the same for both layouts. – Charlie – 2018-10-15T07:15:53.773

@Charlie Fixed it. – Οurous – 2018-10-15T08:30:58.243

1

05AB1E, 42 41 bytes

•Hhç₁d©u÷^Σ(“ðΣèõĆ
-•184в2äεIÇk}ʒ®å_}>T%Ù

Port of @dylnan's Jelly answer.

Try it online or verify all test cases.

Explanation:

•Hhç₁d©u÷^Σ(“ðΣèõĆ
-•184в           # Compressed list [33,34,183,36,37,38,47,40,41,61,33,64,35,36,37,94,38,42,40,41]
      2ä         # Split into two parts: [[33,34,183,36,37,38,47,40,41,61],[33,64,35,36,37,94,38,42,40,41]]
ε   }            # Map each inner list to:
 IÇ              #  Get the input, and convert each character to its unicode value
   k             #  Then get the index of each unicode value in the current map-list
                 #  (this results in -1 if the item doesn't exist)
     ʒ   }       # Filter the resulting list of indices by:
      ®å_        #  If the inner list does not contain any -1
          >      # Increase each index by 1 to make it from 0-indexed to 1-indexed
           T%    # Take modulo-10 to convert 10 to 0
             Ù   # Uniquify the result-lists (and output implicitly)

See this 05AB1E tip of mine (section How to compress integer lists?) to understand why •Hhç₁d©u÷^Σ(“ðΣèõĆ\n-•184в is [33,34,183,36,37,38,47,40,41,61,33,64,35,36,37,94,38,42,40,41]). This (together with the ) is 1 byte shorter than taking the unicode values of the string: '""!ÿ·$%&/()=""!@#$%^&*()"‚Ç.

Kevin Cruijssen

Posted 2018-10-12T20:34:41.550

Reputation: 67 575

The !$ and !!$$%% cases should output only one number as the result is the same for both layouts and there is no ambiguity. – Charlie – 2018-10-15T07:17:38.253

@Charlie Oops, fixed – Kevin Cruijssen – 2018-10-15T07:21:05.647

1

Ruby, 68 bytes

->s{%w[=!"·$%&/() )!@#$%^&*(].map{|a|s.tr a,'0-9'}.grep_v(/\D/)|[]}

Try it online!

G B

Posted 2018-10-12T20:34:41.550

Reputation: 11 099

1

APL (Dyalog), 40 bytes

Anonymous tacit prefix function. Though unused, · is in the Dyalog single byte character set. Assumes 0-based indexing (⎕IO←0) which is default on many systems.

{∪⍵/⍨~10∊¨⍵}'=!"·$%&/()' ')!@#$%^&*('⍳¨⊂

Try it online!

 the entire argument

'=!"·$%&/()' ')!@#$%^&*('⍳¨ indices of the characters in each of these strings

{∪⍵/⍨~10∊¨⍵} apply the following lambda ( is the argument):

10∊¨⍵ for each list of digits, whether 10 (indicating "not found") is a member thereof

~ local negation (i.e. only those where all digits are found)

⍵/⍨ filter the argument by that

 find the unique elements of that

Adám

Posted 2018-10-12T20:34:41.550

Reputation: 37 779

0

Dart, 125 bytes

f(s)=>['=!"·\$%&/()',')!@#\$%^&*('].map((b)=>s.split('').map((e)=>b.indexOf(e)).join()).where((e)=>!e.contains('-')).toSet();

Ungolfed :

f(s){
  ['=!"·\$%&/()',')!@#\$%^&*(']
    .map(
      (b)=>s.split('').map((e)=>b.indexOf(e))
      .join()
    ).where(
      (e)=>!e.contains('-')
    ).toSet();
}
  • Creates an array with the two specified key values, from 0 to 9
  • For each of them, convert the input string to the corresponding number by using the characters' indexes
  • Join the resulting array to create a number
  • Remove any number having a '-' (Dart returns -1 when indexOf can't find a char)
  • Return as a Set to remove duplicates

Try it on Dartpad!

Elcan

Posted 2018-10-12T20:34:41.550

Reputation: 913

0

T-SQL, 143 bytes

SELECT DISTINCT*FROM(SELECT TRY_CAST(TRANSLATE(v,m,'1234567890')as INT)a
FROM i,(VALUES('!"·$%&/()='),('!@#$%^&*()'))t(m))b
WHERE a IS NOT NULL

Input is taken via pre-existing table i with varchar field v, per our IO standards.

Joins the input table with the two different character strings, then uses the new SQL 2017 function TRANSLATE to swap out individual characters, and TRY_CAST to see if we end up with a number. If not, TRY_CAST returns NULL.

The final outer SELECT DISTINCT combines identical results and filters out the NULLS.

BradC

Posted 2018-10-12T20:34:41.550

Reputation: 6 099