10
\$\begingroup\$

Purpose

The idea is to provide the code necessary to map a 32-bit integer to/from a pronouncable word of 9 characters maximum. That could be useful, for example, to make a serial number easier to remember, or type in a form.

Both the method for translating an integer to the corresponding word and for translating back a word to the corresponding integer are required.

Rules

There must be a one-to-one mapping between integers and words, and the whole set of 32-bit integers (or, put in another way, any integer from 0 to 4294967295) must be mappable. Although, obviously, not all words will be meaningful, and inputting words that don't map to an integer may have unspecified behavior.

You are free to decide on exactly which set of "pronouncable" words is meaningful, and how the mapping is done, but words must at least follow these rules:

  • Only the basic 26 letters (A...Z) should be used as characters. Accents, casing, etc... should not be used to extend the possible combinations.
  • 9 characters maximum per word.
  • two consonants (BCDFGHJKLMNPQRSTVWXZ - 20 possiblities) should not be placed next to each other (they must be surrounded by vowels).
  • two vowels (AEIOUY - 6 possibilities) should not be placed next to each other (they must be surrounded by consonants).

Note: the simplest scheme where you have all words constructed as CVCVCVCVC (C being a consonant and V a vowel) gives 4147200000 combinations, and a 32 bit integer has 4294967296 possible values, so it is not enough. You need to expand the number of combinations, either by allowing shorter words, or by allowing VCVCVCVCV combinations, as well.

Other standard rules apply, and standard loopholes are forbidden.

Inputs/Outputs

For each submission, two pieces of code must be provided:

  • One that takes an integer as argument/input and returns/prints the corresponding word
  • One that takes a word as argument/input and returns/prints the corresponding integer

Alternatively, you can choose to submit a single piece of code that handles both operations:

  • When given an integer as an input, it outputs the corresponding word
  • When given a string as an input, it outputs the corresponding integer

Winning condition

This is a , the answer which has the fewest bytes (when summing both pieces of code, for solutions opting for the separated pieces of code) wins.

\$\endgroup\$
  • \$\begingroup\$ Are there any space or time constraints? Do we have to fit within 32GB of memory? \$\endgroup\$ – John Dvorak Apr 6 '17 at 7:47
  • \$\begingroup\$ @JanDvorak Well, we should be able to test your program on a "standard" computer. But the algorithm should be simple, what do you have in mind that requires such a massive amount of memory? \$\endgroup\$ – dim Apr 6 '17 at 7:49
  • \$\begingroup\$ I could just generate all possible nine-letter words that match your formula and then index into the set or do a binary search. \$\endgroup\$ – John Dvorak Apr 6 '17 at 7:51
  • \$\begingroup\$ @JanDvorak I must admit I didn't think of that. I was more thinking about solutions that were basically doing base-26 conversions, with some adjustments to satisfy the vowel/consonant constraint. But I somehow doubt the "brutal" way you had in mind can be code-golf efficient. Anyway, if I must really clarify this, let's say you are not allowed to allocate more than 4GB of memory. \$\endgroup\$ – dim Apr 6 '17 at 8:00
  • \$\begingroup\$ You could require the answerers to run their code for some predetermined values (0,1,10,2**32-1 and such) and then back and to include the results in the answer. \$\endgroup\$ – John Dvorak Apr 6 '17 at 8:03
1
\$\begingroup\$

JavaScript (ES6), 205 bytes

p=>(a='bcdfghjklmnpqrstvwxzaeiouy',1/p)?[...Array(9)].map(_=>r=a[p%(n=26-n)+(p=p/n|0,n<7)*20]+r,n=p>(p%=4e9)?20:6,r='')&&r:[...p].map(c=>r=r*(n=26-n)+a.search(c)%20,n=a.search(p[r=0])<20?6:20)&&r+(n<7)*4e9

Cutoff point between CVCVCVCVC and VCVCVCVCV is 4e9, so starts going wrong at 5244160000 (numeric input) or zesuwurib (string input).

\$\endgroup\$
  • \$\begingroup\$ Six months later... I award you the accept points, since you're the shortest (and I can't accept rturnbull's answer, which does not satisfies the clarification I made in the comments). \$\endgroup\$ – dim Sep 11 '17 at 18:40
2
\$\begingroup\$

PHP, 353 Bytes

Encoding + Decoding

is_numeric($argn) contains the boolean. It is true if the input is an integer.

$c=array_diff(range(A,Z),$v=[A,E,I,O,U,Y]);sort($c);if(is_numeric($a=$argn)){$r=($a)%26<6?$v[$a%26]:$c[$a%26-6];$a=$a/26^0;while($a){$z=count($t=in_array($r[0],$v)?$c:$v);$r=$t[$n=$a%$z].$r;$a=$a/$z^0;}echo$r;}else{for($p=1;$i++<strlen($a);){$u=($b=in_array($a[-$i],$c))?$c:$v;$s+=array_flip($u)[$a[-$i]]*$p+($b&$i<2?6:0);$p*=$i>1?count($u):26;}echo$s;}

PHP, 190 Bytes (Encode) + 195 Byte (Decode) = 385 Bytes

Encoding

$c=array_diff(range(A,Z),$v=[A,E,I,O,U,Y]);sort($c);$r=($a=$argn)%26<6?$v[$a%26]:$c[$a%26-6];$a=$a/26^0;while($a){$z=count($t=in_array($r[0],$v)?$c:$v);$r=$t[$n=$a%$z].$r;$a=$a/$z^0;}echo$r;

5391360000=26*120**4 combinations are available

Online Version Encoding without E_NOTICE

Expanded

$c=array_diff(range(A,Z),$v=[A,E,I,O,U,Y]);
sort($c); # End of Prepare the two array
$r=($a=$argn)%26<6?$v[$a%26]:$c[$a%26-6]; #base 26 decision input mod 26 <6 end with vowel
$a=$a/26^0; #integer division input with 26
while($a){
    $z=count($t=in_array($r[0],$v)?$c:$v); # use vowel if last entry is consonant and viceversa
    $r=$t[$n=$a%$z].$r; # base 6 or base 20 decision
    $a=$a/$z^0; # divide through base
}echo$r; # Output result

Input => Output

4294967296 => TYPYQACOV 
333 => DAT 
1 => E 
7 => C 
4294967276 => UTOPOQAMI

If you need always 9 Byte result please replace while($a) with while(strlen($r)<9) + 10 Bytes

Decoding

$c=array_diff(range(A,Z),$v=[A,E,I,O,U,Y]);sort($c);for($p=1;$i++<strlen($a=$argn);){$u=($b=in_array($a[-$i],$c))?$c:$v;$s+=array_flip($u)[$a[-$i]]*$p+($b&$i<2?6:0);$p*=$i>1?count($u):26;}echo$s;

Expanded

$c=array_diff(range("A","Z"),$v=["A","E","I","O","U","Y"]);
sort($c); # End of Prepare the two array
for($p=1;$i++<strlen($a=$argn);){ 
    $u=($b=in_array($a[-$i],$c))?$c:$v; # find use array for $a[-$i]
    $s+=array_flip($u)[$a[-$i]]*$p+($b&$i<2?6:0); # sum value
    $p*=$i>1?count($u):26; # raise multiple for next item
}echo$s;

Input => Output

ABABABABE => 1
E => 1
UTOPOQAMI => 4294967276
BABABADAT => 333
DAT => 333
TYPYQACOV => 4294967296

Online Version Decoding without E_NOTICE

Additional Check

If we need a check if a string is valid.

Add $x.=$b?:0; in the end of the decoding loop + 10 Bytes

Replace echo$s; with echo!preg_match('#([01])\1$#',$x)?$s:_; + 32 Bytes

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

R, 165 bytes

Encoding and decoding in one function.

This function uses the brute-force method of creating all possible values and then simply returning the index when it's given string input and returning the string when given integer input. As a consequence, it's very slow and uses 16GB+ of memory!

function(x){i=c(1,5,9,15,21,25)
d=apply(expand.grid(c<-letters[-i],v<-letters[i],c,v,c,v,c,v,c(c,"")),1,paste,collapse="")
`if`(mode(x)=="numeric",d[x],which(d==x))}

4,354,560,000 values are possible. This covers all strings of form CVCVCVCV(C), with the last C being optional.

\$\endgroup\$
  • \$\begingroup\$ @mbomb007 Gigabytes, sorry for the typo. The function does both encoding and decoding depending on whether the argument is a string or an integer. I've updated the post to clarify that. \$\endgroup\$ – rturnbull Apr 6 '17 at 14:22
  • \$\begingroup\$ Can the downvoter leave a comment to suggest improvement? Thanks. \$\endgroup\$ – rturnbull Apr 6 '17 at 18:59
  • 1
    \$\begingroup\$ In the comments of the question, dim clarifies that you cannot use more than 4gb of memory.... \$\endgroup\$ – Socratic Phoenix Apr 24 '17 at 15:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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