24
\$\begingroup\$

Room Number Locator

I have come across an interesting problem solving technique at my job when given the wrong room number from a colleague for a meeting. Every now and then, while on the way to a meeting, a member on my team will send me the wrong room number, typically because they are in a rush at their desk and fat finger the wrong key.

Interestingly, upon arrival at the wrong room, I typically can guess which room they really meant by imagining a Numeric Keypad:

and by guessing an adjacent number they meant to press.

Challenge

Your challenge is to write a function that takes a building office number (000-999) and outputs the possible typo solutions, assuming your colleague only mistypes one digit.

The following table shows which numbers are adjacent to each other on a Numeric Keypad:

0 -> 1,2
1 -> 0,2,4
2 -> 0,1,3,5
3 -> 2,6
4 -> 1,5,7
5 -> 2,4,6,8
6 -> 3,5,9
7 -> 4,8
8 -> 5,7,9
9 -> 6,8

Input

A 3 digit number: 000-999. Assume input of exactly 3 digits. If the number is less than 100 or less than 10, you will be given the leading zeros. (i.e. 004 & 028).

Output

A list of possible rooms. This can be in any form you want, as long as there is a delimiter between room numbers. (i.e. space, comma, new line, etc..) If the number is less than 100 or less than 10, you can or cannot have the leading zeros as output, that is up to you. (i.e. 004 can be 004 04 4, and 028 can be 028 28)

Test Cases(leading zeros are optional):

008 -> 108, 208, 018, 028, 005, 007, 009 
123 -> 023, 223, 423, 103, 113, 133, 153, 122, 126
585 -> 285, 485, 685, 885, 555, 575, 595, 582, 584, 586, 588
777 -> 477, 877, 747, 787, 774, 778
963 -> 663, 863, 933, 953, 993, 962, 966
555 -> 255, 455, 655, 855, 525, 545, 565, 585, 552, 554, 556, 558

This is , so the shortest code in bytes for each language wins.

\$\endgroup\$
15
  • 1
    \$\begingroup\$ Can we take input as a list of three digits (0-9)? \$\endgroup\$
    – hyperneutrino
    Commented Jan 19, 2018 at 15:19
  • 9
    \$\begingroup\$ ...and this is why meeting rooms should have names. \$\endgroup\$ Commented Jan 19, 2018 at 16:35
  • 2
    \$\begingroup\$ @JonathanAllan It's a lot harder for new people to find "Dolphin Room" than "Room 218" (assuming that room numbers are assigned in order). A compromise would be alphabetically ordering the names, but then you only have 26. \$\endgroup\$
    – Andrew
    Commented Jan 19, 2018 at 19:57
  • 1
    \$\begingroup\$ @KellyLowder should have been 933 so I've fixed it up. \$\endgroup\$ Commented Jan 19, 2018 at 20:22
  • 4
    \$\begingroup\$ Related, I once worked in IT where there was a professor who had trouble with the room technology several weeks running. He was in Bradley 210 (which I knew, Bradley being the name of the building. The building next door--Matheson--was connected via a sky bridge on the 3rd floor. Bradley was 5 stories tall, Matheson 4). He could never tell me what room he was in correctly. One time he told me he was in "Matheson 605" which patently didn't exist, and got none of the digits right. \$\endgroup\$ Commented Jan 19, 2018 at 22:26

15 Answers 15

13
\$\begingroup\$

Wolfram Language (Mathematica), 112 106 bytes

Recognizing that a numeric keypad is basically a 3x3 GridGraph with edges added for 0, we get the adjacent digits for each input digit with AdjacencyList.

This can be seen below:

EdgeAdd[GridGraph[{3,3},VertexLabels->"Name",GraphLayout->"SpringEmbedding"],{0<->1,0<->2}] yields:

enter image description here

Then I use Tuples to figure out all the possible mistakes and pick out those with exactly one error with Select and EditDistance. By the way, this will work for longer room numbers and you can also increase the EditDistance parameter to allow for more than one error. Might be able to golf this down a little further but wanted to show my approach.

h@u_:=Select[Tuples[AdjacencyList[EdgeAdd[GridGraph[{3,3}],{0<->1,0<->2}],#]~Join~{#}&/@u],#~EditDistance~u==1&]

Slightly more golfed version hardcoded to length 3 room numbers (106 Bytes). This will output as a rank 3 list corresponding to each digit:

Thread/@ReplacePart[#~Table~3,{i_,i_}:>(AdjacencyList[GridGraph@{3,3}~EdgeAdd~{0<->1,0<->2},#]&/@#)[[i]]]&

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ One could also use other distance functions such as DamerauLevenshteinDistance instead of EditDistance which would also include transposition errors. \$\endgroup\$ Commented Jan 19, 2018 at 22:19
9
\$\begingroup\$

Python 2, 89 bytes

lambda r:[r[:i]+[c]+r[i+1:]for i,n in enumerate(r)for c in`ord(u'ÌЋ>তŧ0ɃD'[n])`]

Try it online!

The 1st and 5th characters may not being displayed here (browser dependent), but the full string is equivalent to [21, 204, 1035, 62, 157, 2468, 359, 48, 579, 68]

\$\endgroup\$
0
4
\$\begingroup\$

05AB1E, 29 bytes

v•4TË\ªye-³—Ïʒ••Ćδn¼•S£yèʒNǝ,

Try it online!

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

R, 190 bytes

function(x){l=list(c(1,2),c(0,2,4),c(0,1,3,5),c(2,6),c(1,5,7),c(2,4,6,8),c(3,5,9),c(4,8),c(5,7,9),c(6,8))
a=do.call(expand.grid, mapply(c,l[x+1],x))
a[apply(a,1,function(y){sum(x==y)==2}),]}

Try it online!


My second attempt at CodeGolf! Pretty long, 190 bytes, but the best I could manage with R. Curious to see if others have feedback or can do better!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ a whole bunch of little things: you have an extra space in the second line; abusing the precedence of : over */+- can shave off a few bytes in the first line, getting rid of do.call, treating a as a matrix and transposing it saves all around 39 bytes: Try it online! \$\endgroup\$
    – Giuseppe
    Commented Jan 21, 2018 at 19:05
  • \$\begingroup\$ You are good at this! Thanks for the feedback. \$\endgroup\$
    – Florian
    Commented Jan 21, 2018 at 19:16
2
\$\begingroup\$

JavaScript (Firefox 30-57), 115 109 bytes

f=([c,...a],p=``)=>c?[...(for(n of``+[12,240,1350,26,157,2468,359,48,579,68][c])p+n+a.join``),...f(a,p+c)]:[]

Edit: Saved 6 bytes thanks to @edc65 (although suggested 0s now appear after other suggestions). ES6 version, 118 112 bytes:

f=([c,...a],p=``)=>c?[...[...``+[12,240,1350,26,157,2468,359,48,579,68][c]].map(n=>p+n+a.join``),...f(a,p+c)]:[]
<input oninput=o.textContent=f(this.value).join`\n`><pre id=o>

\$\endgroup\$
3
  • \$\begingroup\$ I see this [for(...)] in a lot of code golfs, but I don't fully understand it, and I can't seem to find it in any documentation. Could you explain it or post a link to an explanation? \$\endgroup\$ Commented Jan 21, 2018 at 21:02
  • \$\begingroup\$ save 6 bytes [...[12,240,1350,26,157,2468,359,48,579,78][c]+''] \$\endgroup\$
    – edc65
    Commented Jan 21, 2018 at 21:14
  • 1
    \$\begingroup\$ @AntonBallmaier [for(...)] was one of several array comprehension syntax proposals that never made it into ECMAscript. It allowed you to loop over an iterator and succinctly filter and/or map over the results. (I found it particularly useful when doing double iteration.) \$\endgroup\$
    – Neil
    Commented Jan 21, 2018 at 23:01
2
\$\begingroup\$

Java, 205 177 bytes

b->{for(int c=0;c<3;c++){char[]d=b.toCharArray();for(char e:"12,024,0135,26,157,2468,359,48,579,68".split(",")[new Byte(""+d[c])].toCharArray()){d[c]=e;System.out.println(d);}}}

I know it's long compared to the other answers. My excuse: it's in Java.
Oracle should rename toCharArray to something like getCrs.

Credits

-28 characters by Kevin Cruijssen

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Some small things to golf. (String b)-> can be just b->, and you can remove the trailing ;. As for actual things to golf: You only use a once, so you can remove String[]a=...; and use "12,024,0135,26,157,2468,359,48,579,68".split(",")[...] directly. Also, Byte.parseByte can be new Byte. In total: 177 bytes. \$\endgroup\$ Commented Jan 22, 2018 at 8:18
  • 1
    \$\begingroup\$ @KevinCruijssen thanks, those are some tricks I'll have to learn :) \$\endgroup\$ Commented Jan 22, 2018 at 14:40
  • 1
    \$\begingroup\$ Tips for golfing in Java and Tips for golfing in <all languages> might be interesting to read through in case you haven't yet. :) \$\endgroup\$ Commented Jan 22, 2018 at 14:55
2
\$\begingroup\$

Ruby 97 bytes

->i{c=0;i.map{|j|[12,204,1035,26,157,2468,359,48,579,68][j].digits.map{|k|f=*i;f[c]=k;p f};c+=1}}

Try it online!

Alternatively, 94 chars but 100 bytes

->i{c=0;i.map{|j|"\fÌЋ\u001A\u009Dতŧ0ɃD".unpack("U*")[j].digits.map{|k|f=*i;f[c]=k;p f};c+=1}}

Try it online!

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

C (gcc), 136 or 114 bytes

ASCII version 136 bytes

m[]={12,240,1350,26,157,2468,359,48,579,68},p,i,X=10;f(n){for(i=100;i;i/=X)for(p=m[n/i%X];p;p/=X)printf("%d ",n/(i*X)*(i*X)+p%X*i+n%i);}

Try it online!

Unicode 114 108 bytes (TiO seems to count weirdly for this)

Thanks to @ceilingcat for this version.

p,i,X=10;f(n){for(i=1e3;i/=X;)for(p=L"\fðՆ\32\x9dতŧ0ɃD"[n/i%X];p;p/=X)printf("%d ",n/i/X*i*X+p%X*i+n%i);}

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ @ceilingcat Hm. TiO says 108 bytes. \$\endgroup\$
    – gastropner
    Commented Nov 28, 2019 at 5:03
  • \$\begingroup\$ I don’t think TIO counts UTF-8 bytes correctly in C. Try changing the language to bash or something else and watch the byte count change. \$\endgroup\$
    – ceilingcat
    Commented Nov 28, 2019 at 6:46
  • \$\begingroup\$ @ceilingcat Yeah, was wonky locally too. Saved file is 114, true enough. \$\endgroup\$
    – gastropner
    Commented Nov 28, 2019 at 7:33
1
\$\begingroup\$

Perl 5, 120 85 + 2 (-F) = 87 bytes

map{@,=@F;$,[$i]=$_,say@,for(12,240,1350,26,157,2468,359,48,579,68)[$_]=~/./g;$i++}@F

Try it online!

Saved 35 bytes by borrowing an idea from @AsoneTuhid's ruby answer.

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

Python 2, 103 bytes

thanks to @Lynn for -4 bytes.

lambda n:{n[:i]+r+n[i+1:]for i,v in enumerate(n)for r in`0x134cd9a07d1e58feab643f7db24102`[int(v)::10]}

Try it online!

\$\endgroup\$
1
  • \$\begingroup\$ Save 4 bytes with: in`0x134cd9a07d1e58feab643f7db24102`[int(v)::10] (I tried int('…',36) too but it’s one byte longer.) \$\endgroup\$
    – lynn
    Commented Jan 19, 2018 at 21:03
1
\$\begingroup\$

Julia 0.6, 93 bytes

~r=[(R=copy(r);R[j]=i;R)for i=0:9,j=1:3 if(big(1)<<(i+10r[j]))&0x502A044228550A21102B05406>0]

Try it online!

  • Takes a vector of digits and returns a list in the same format.
  • 0x502A044228550A21102B05406 is a UInt128 in which the 1+10jth bit is set iff i is next to j on the numpad.
  • big(1) is a BigInt. It is used to prevent overflow and uses less characters than Int128(1) or UInt128(1).
\$\endgroup\$
1
\$\begingroup\$

Jelly, 35 bytes

ḷþị“-ⱮⱮVḟ|żṣ~ẋ³ɱgẆ’ḃ⁽¦ḳ¤$ṛ¦DŒp$¥"JẎ

Try it online!

-1 thanks to Jonathan Allan.

Explanation being updated...

\$\endgroup\$
3
  • 3
    \$\begingroup\$ I honestly have no clue about how this is parsed, let alone how it works. An explanation would be greatly appreciated. \$\endgroup\$ Commented Jan 19, 2018 at 22:32
  • \$\begingroup\$ @cairdcoinheringaahing sorry, no time now \$\endgroup\$ Commented Jan 19, 2018 at 22:44
  • \$\begingroup\$ -1 byte: Wẋ3 -> ḷþ \$\endgroup\$ Commented Jan 21, 2018 at 20:33
1
\$\begingroup\$

SQL (SQLite), 533 bytes

with m as (select 0 as i, 1 as o union values (0,2),(1,0),(1,2),(1,4),(2,0),(2,1),(2,3),(2,5),(3,2),(3,6),(4,1),(4,5),(4,7),(5,2),(5,4),(5,6),(5,8),(6,3),(6,5),(6,9),(7,4),(7,8),(8,5),(8,7),(8,9),(9,6),(9,8))select o || substr('008', 2, 1) || substr('008', 3, 1)from m where substr('008', 1, 1) = cast(i as text)union select substr('008', 1, 1) || o || substr('008', 3, 1)from m where substr('008', 2, 1) = cast(i as text)union select substr('008', 1, 1) || substr('008', 2, 1) || o from m where substr('008', 3, 1) = cast(i as text)

Ungolfed

with m as (
    select 0 as i, 1 as o
    union
    values
    /*(0,1),*/(0,2),
    (1,0),(1,2),(1,4),
    (2,0),(2,1),(2,3),(2,5),
    (3,2),(3,6),
    (4,1),(4,5),(4,7),
    (5,2),(5,4),(5,6),(5,8),
    (6,3),(6,5),(6,9),
    (7,4),(7,8),
    (8,5),(8,7),(8,9),
    (9,6),(9,8)
)
select o || substr(s, 2, 1) || substr(s, 3, 1)
from m, t
where substr(s, 1, 1) = cast(i as text)
union
select substr(s, 1, 1) || o || substr(s, 3, 1)
from m, t
where substr(s, 2, 1) = cast(i as text)
union
select substr(s, 1, 1) || substr(s, 2, 1) || o
from m, t
where substr(s, 3, 1) = cast(i as text)

Explanation

The input is a single text row on table t with column s. My understanding is that according to this meta answer this is an acceptable form of input. The input can be created as below.

drop table if exists t;
create table t (s text);
insert into t values('555'); -- Your input here

Annotated solution

with m as ( -- Using this in the "with" allows us to only type is once
    select 0 as i, 1 as o -- The first pair is here and it names the columns
    union
    values
    /*(0,1),*/(0,2),
    (1,0),(1,2),(1,4),
    (2,0),(2,1),(2,3),(2,5),
    (3,2),(3,6),
    (4,1),(4,5),(4,7),
    (5,2),(5,4),(5,6),(5,8),
    (6,3),(6,5),(6,9),
    (7,4),(7,8),
    (8,5),(8,7),(8,9),
    (9,6),(9,8)
)
select o || substr(s, 2, 1) || substr(s, 3, 1) -- concat the first wrong char with two correct chars
from m, t
where substr(s, 1, 1) = cast(i as text) -- when the first char is in the i (input) column from above
union
select substr(s, 1, 1) || o || substr(s, 3, 1)
from m, t
where substr(s, 2, 1) = cast(i as text)
union
select substr(s, 1, 1) || substr(s, 2, 1) || o
from m, t
where substr(s, 3, 1) = cast(i as text)
\$\endgroup\$
1
\$\begingroup\$

Kotlin, 117 bytes

mapIndexed{i,c->"12,024,0135,26,157,2468,359,48,579,68".split(",")[c-'0'].map{replaceRange(i,i+1,it+"")}}.flatMap{it}

Beautified

mapIndexed { i, c ->
    "12,024,0135,26,157,2468,359,48,579,68"
        .split(",")[c - '0']
        .map { replaceRange(i, i + 1, it + "") }
}.flatMap { it }

Test

fun String.f(): List<String> =
mapIndexed{i,c->"12,024,0135,26,157,2468,359,48,579,68".split(",")[c-'0'].map{replaceRange(i,i+1,it+"")}}.flatMap{it}

data class Test(val input:Int, val answers: List<Int>)

val tests = listOf(
    Test(8, listOf(108, 208, 18, 28, 5, 7, 9)),
    Test(123, listOf(23, 223, 423, 103, 113, 133, 153, 122, 126)),
    Test(585, listOf(285, 485, 685, 885, 555, 575, 595, 582, 584, 586, 588)),
    Test(777, listOf(477, 877, 747, 787, 774, 778)),
    Test(963, listOf(663, 863, 933, 953, 993, 962, 966)),
    Test(555, listOf(255, 455, 655, 855, 525, 545, 565, 585, 552, 554, 556, 558))
)

fun main(args: Array<String>) {
    for (r in tests) {
        val input = r.input.toString().padStart(3, '0')
        val expected = r.answers.map { it.toString().padStart(3, '0') }.sorted()
        val actual = input.f().sorted()
        if (expected != actual) {
            throw AssertionError("$input -> $actual | $expected")
        }
    }
}

TIO

TryItOnline

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

T-SQL, 322 bytes

WITH m AS(SELECT LEFT(value,1)i,RIGHT(value,1)o FROM STRING_SPLIT('01,02,10,12,14,20,21,23,25,32,36,41,45,47,52,54,56,58,63,65,69,74,78,85,87,89,96,98',','))SELECT o+RIGHT(s,2)FROM t,m WHERE i=LEFT(s,1)UNION SELECT LEFT(s,1)+o+RIGHT(s,1)FROM t,m WHERE i=SUBSTRING(s,2,1)UNION SELECT LEFT(s,2)+o FROM t,m WHERE i=RIGHT(s,1)

The input is taken from the column s of a single-row table named t:

DROP TABLE IF EXISTS t
CREATE TABLE t (s CHAR(3))
INSERT INTO t VALUES('008')

Ungolfed:

WITH m AS (
    SELECT LEFT(value,1) i, RIGHT(value,1) o
    FROM STRING_SPLIT('01,02,10,12,14,20,21,23,25,32,36,41,45,47,52,54,56,58,63,65,69,74,78,85,87,89,96,98',',')
)
SELECT o+RIGHT(s,2) FROM t,m WHERE i=LEFT(s,1)
UNION
SELECT LEFT(s,1)+o+RIGHT(s,1) FROM t,m WHERE i=SUBSTRING(s,2,1)
UNION
SELECT LEFT(s,2)+o FROM t,m WHERE i=RIGHT(s,1)

SQLFiddle

\$\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.