Should we be friends?

30

6

Note this is a question primarily focusing on

Introduction

Bacefook wants people to be friendlier! As such, they are implementing a new system to suggest friends! Your task is to help Bacefook to implement their new suggesting system.

Specifications:

Your program must be a REPL (read-eval-print loop) supporting 3 types of command: FRIEND, SUGGEST and KNOW.

FRIEND X Y - Specifies that X and Y are friends in the social network.

  • If X is friends with Y, then Y is friends with X

  • Can, but doesn't have to have output

  • X is always friends with X

KNOW X Y - Output a truthy value if X and Y are friends, falsy otherwise

  • KNOW X X will always output a truthy value

SUGGEST X Y - Output a truthy value if X and Y should be friends, falsy otherwise. X and Y should be friends if:

  • X and Y are not friends

  • X and Y have at least 1 friend in common

You are allowed to replace FRIEND, SUGGEST and KNOW with your own strings, but you must mention what string you have replaced each command with.

Your program can take in input/produce output in any way desirable, as long as it is reasonably easy to recognize how it works.

The number of people in the social network N is between 1 and 100,000, but there may be any number of "friend links" (edges).

If you haven't noticed yet, this is a graph searching problem. The (likely) easiest (and possibly fastest) data structure to implement this in would be an adjacency matrix.

Test cases

FRIEND A B
FRIEND A C
FRIEND B D
SUGGEST A B -> Falsy, as they are friends
SUGGEST A D -> Truthy, as they share B as a common friend
SUGGEST C D -> Falsy, they do not share a common friend
KNOW D B -> Truthy, they are friends
KNOW B C -> Falsy, not friends
=============
FRIEND Tom Tim
KNOW Tom Tim -> Truthy
KNOW Tim Tom -> Truthy
KNOW Tom Kit -> Falsy
=============
KNOW Tim Kit -> Falsy
FRIEND Tim Tom
KNOW Tim Kit -> Falsy
FRIEND Tom Kit
SUGGEST Tim Kit -> Truthy
=============
FRIEND X Y
SUGGEST X Y -> Falsy since X is friends with X

Here's some more test cases in image form

Win condition

This is , shortest code wins!

Thunda

Posted 2017-03-29T04:08:02.937

Reputation: 1 151

So for example, can we start by inputting a list of all people in the network, like {A, B, C, D}? – Greg Martin – 2017-03-29T05:21:04.667

@GregMartin Nope. That's the fun part of the challenge :) – Thunda – 2017-03-29T05:41:05.037

2Having the test cases in text form would be way more helpful. – Greg Martin – 2017-03-29T06:41:26.840

@GregMartin Would you rather I put them such that all of the input statements are in 1 block and all of the output statements are in another for easy comparison? Or the arrows are fine? – Thunda – 2017-03-29T07:09:27.800

Thanks! While my personal preference is to separate the input statements from the output statements, I feel I'm in the PPCG minority and more people prefer the arrow versions that you've made. – Greg Martin – 2017-03-29T07:13:45.583

This challenge screams Prolog. – orlp – 2017-03-29T07:51:44.913

When you say "your program must be a REPL" does that mean it could use the REPL of the language's interpreter, as is done in the Prolog answer? or does the program have to implement the reading, evaluating, and printing itself? the wording does not make it perfectly clear. – quintopia – 2017-03-29T08:48:39.387

1Can we have output after the FRIEND command? – ovs – 2017-03-29T08:52:55.067

@ovs I'll... go with yes – Thunda – 2017-03-29T10:48:58.120

@quintopia It can use the language's interpreter. Useful in languages like Haskell or in this case Prolog, but an extra problem for C-type languages. – Thunda – 2017-03-29T10:57:42.130

7SUGGEST UK EU. – WBT – 2017-03-29T17:27:34.937

1@Thunda in Python, using the built-in REPL requires two extra characters in the command. Should languages like this add those extra bytes to the total length of the program? – quintopia – 2017-03-29T18:24:57.200

@quintopia Yes. – Thunda – 2017-03-30T02:35:41.050

@thedarkwanderer FRIEND X Y SUGGEST X X should return falsy because X is friends with X, it's inferred from KNOW X X is truthy. I'll add in a clarification. – Thunda – 2017-03-30T06:21:26.523

@Thunda ah, I figured know X X being truthy was merely special behaviour. Makes more sense. – Please stop being evil – 2017-03-30T07:45:37.340

@WBT ==> null – OldBunny2800 – 2017-04-03T20:14:40.250

Answers

44

SWI-Prolog, 62 47 41 bytes

X*Y:-X+Y;Y+X;X==Y.
X?Y:-not(X*Y),X*Z,Y*Z.

Prolog isn't too often useful, but when it is it's just beautiful. We'll use a+b to notate that a is friends with b, a*b that a knows b and a?b that b should be suggested to a or not. The first line simply says that X*Y is true if either X+Y, Y+X or X == Y is true. This implements the symmetry of knowing eachother. Asking if there should be a suggestion is incredibly simple. We just ask if there is a Z such that X*Y is false and X*Z and Y*Z is true. Exactly as described in the challenge.

If you save this as a file (e.g. friends.pl) and open SWI-Prolog with this file (prolog -l friends.pl) you get dropped into a REPL.

You can assert friendships like this:

assert('a' + 'b').
assert('a' + 'c').
assert('b' + 'd').

You can check if people know eachother or suggestion should be made:

'a'*'b'.
'a'?'d'.

orlp

Posted 2017-03-29T04:08:02.937

Reputation: 37 067

You should be able to save a bunch of bytes replacing k(X,Y) with X*Y and the same with f and s using different operands. 21 bytes if I've counted correctly. – Emigna – 2017-03-29T08:48:54.520

Not sure how they work with asserts though, so I'm unsure about f. – Emigna – 2017-03-29T08:57:12.333

12Completely farts through the data structure designing part of the question. Amazing. – Thunda – 2017-03-29T10:49:46.713

@Emigna I've implemented it, but it didn't save as much as you counted. – orlp – 2017-03-29T16:08:36.180

I tested it like this at 41 bytes. I don't have a REPL to try it in though so I don't know if it works different there.

– Emigna – 2017-03-29T16:16:51.323

@Emigna Ah, I'm not too familiar with Prolog so I didn't know you could define an infix predicate like that. – orlp – 2017-03-29T17:15:15.583

I didn't know it either before I started golfing on this site (prolog was the language I first golfed in). It is a pretty cool feature. – Emigna – 2017-03-29T17:19:22.617

15

PHP, 138 133 129 bytes

PHP beats Mathematica - a rare occurence.

for(;$s=fgets(STDIN);$s>G?print$$a[$b]?$s<L:$s>L&&@array_intersect_key($$a,$$b):$$a[$b]=$$b[$a]=1)[,$a,$b]=explode(" ",trim($s));

prints 1 for truthy, empty string for falsy. Run with -nr or test it online.
needs PHP 7.1 for the list assignment; user names are case sensitive and should exclude a, b, s.

breakdown

for(;$s=fgets(STDIN);                       # loop through input
    $s>G                                        # 2. evaluate command
        ?print$$a[$b]
            # command KNOW: true if $$a[$b]
            ?$s<L
            # command SUGGEST: true if !$$a[$b] and array_intersect_key returns truthy
            :$s>L&&@array_intersect_key($$a,$$b)
        # command FRIEND: set keys in $$a and $$b
        :$$a[$b]=$$b[$a]=1
)
    [,$a,$b]=explode(" ",trim($s));             # 1. parse user names to $a and $b
  • $s has to be trimmed because it includes the newline character.
  • array_intersect_key has to be muted or it would yield warnings for empty $$a or $$b.
  • +18 +15 bytes for all user names: Replace $$a with $f[$a] and $$b with $f[$b].

Titus

Posted 2017-03-29T04:08:02.937

Reputation: 13 814

12

CMD (Batch), 50 + 20 + 135 = 205 bytes

  • FRIEND.CMD

    @for %%f in (%1.%1 %1.%2 %2.%2 %2.%1)do @set %%f=1
    
  • KNOW.CMD

    @call echo(%%%1.%2%%
    

    Prints 1 for friends, a blank line for strangers.

  • SUGGEST.CMD

    @call set k=0%%%1.%2%%
    @set k=1&if %k%==0 for /f "tokens=2 delims=.=" %%f in ('set %1.')do @call set k=%%k%%%%%%f.%2%%
    @echo(%k:~1,1%
    

    Prints 1 or a blank line. I think six consecutive %s might be a new personal best.

Neil

Posted 2017-03-29T04:08:02.937

Reputation: 95 035

That's crazy awesome. Nice solution. – AdmBorkBork – 2017-03-29T13:30:44.320

6

Python 3, 163 149 143+2=145 bytes

-6 bytes thanks to @FelipeNardiBatista

l=[]
def f(a,b):l.extend([(a,b),(b,a)])
k=lambda a,b:a==b or(a,b)in l
s=lambda a,b:k(a,b)-1and{c for c,d in l if d==a}&{c for c,d in l if d==b}

Save it to a file and run it as python3 -i file.py
Use
- f("a", "b") instead of FRIENDS a b
- k("a", "b") instead of KNOW a b
- s("a", "b") instead of SUGGEST a b

Falsey output : 0, set(), False
Truthy output : non-empty set, True

Try it online


164 bytes when not using python interpreter as REPL:

f=[]
while 1:c,a,b=input().split();i=(a,b)in f;f+=c=="f"and[(a,b),(b,a)]or[(i+(a==b),-i+1and{c for c,d in f if d==a}&{c for c,d in f if d==b})];print(f[-1][c=="s"])

Uses
- f for FRIEND
- s for SUGGEST
- anything else for KNOW

Try it online

ovs

Posted 2017-03-29T04:08:02.937

Reputation: 21 408

Suggest function is broken for the second link – Thunda – 2017-03-29T10:56:09.300

@Thunda fixed it – ovs – 2017-03-29T11:03:02.567

Correct me if I'm missing something, but instead of l.extend([(a,b),(b,a)]), can't you just do l+=[(a,b),(b,a)]? (I haven't tested this yet) – HyperNeutrino – 2017-03-29T12:56:41.647

Oh sorry, I realized my mistake, that causes an UnboundLocalError. Nice answer by the way! – HyperNeutrino – 2017-03-29T12:57:23.403

if you remove bool() from the s function, and use 0, {} and False as Falsey and True and a not empty set as Truthy, you could save 6 bytes – Felipe Nardi Batista – 2017-03-29T14:46:46.393

You can save around 20 bytes with s=lambda a,b:k(a,b)-1and{c for c,d in l if d==a&d==b} Try it online!!

– Keerthana Prabhakaran – 2017-03-30T04:07:10.923

What does the -i do? – caird coinheringaahing – 2017-04-14T23:05:10.667

6

Python 3, 122 118+2=120 bytes

l={}
def f(*r):l[r]=l[r[::-1]]=1
k=lambda a,b:a==b or(a,b)in l
s=lambda a,b:1-k(a,b)and any(k(a,z)&k(b,z)for z,_ in l)

Usage is exactly the same as ovs's answer.

orlp

Posted 2017-03-29T04:08:02.937

Reputation: 37 067

1It's pretty obvious to me, but the requirements say you need to specify how to use your REPL and with what commands. May be useful to those who don't know python. (Incidentally, this is exactly the method I would have used.) – quintopia – 2017-03-29T18:19:47.407

5

Mathematica, 164 bytes

f={}
p:=Union@@f
i=Position[p,#][[1,1]]&
m:=Outer[Boole@MemberQ[f,{##}]&,p,p]
a=(#[[i@#2,i@#3]]/._@__->0)>0&
F=(f=#~Tuples~2~Join~f;)&
K=m~a~##&
S=a[m.m,##]&&!K@##&

Defines three main functions F, S, and K with the desired behavior. For example, the sequence of commands

F@{David, Bob}
F@{Bob, Alex}
F@{Alex, Kitty}
F@{Daniel, David}
F@{David, Kit}
S[David, Alex]
S[Bob, Kitty]
S[David, Kitty]
S[David, Bob]
K[David, Bob]
F@{Kit, Kitty}
S[David, Kitty]

is the last test case from the image linked in the OP; the F commands yield no output (the single semicolon seems a small price to pay for this), while the six S and K commands yield

True
True
False
False
True
True

as desired.

At any moment, f is the list of ordered pairs of the form {A, B} where A knows B, while p is the list of people appearing in some element of f. Calling F@{A, B} adds the four ordered pairs {A, B}, {B, A}, {A, A}, and {B, B} to f.

Also, at any moment, m is the adjacency matrix of the underlying graph (a person is adjacent to themselves and to all their Friends); the rows and columns are indexed by p, and i converts a person to the corresponding row/column number. The helper function a takes a matrix and two people as inputs and looks up the entry of the matrix whose "coordinates" are the two people, returning True if the number is positive and False if it's zero. (It's also possible to call a when one of the input people isn't yet recognized—for example, making a KNOW or SUGGEST query before any FRIEND declarations, or asking about some poor person who has no friends; this throws errors, but the rule /._@__->0 forces the output to be False anyway.)

Calling K[A, B] therefore looks up whether m[A, B] is positive, which implements the Know verb. The matrix product m.m is the length-2-path matrix, containing the number of ways to go from one person to another along a path of length 2; this allows S[A, B] to implement the Suggest verb, as long as we additionally check by hand (&&!K@##) that the input people don't already know each other.

Fun fact: for free, this implementation allows us to declare cliques of friends—the command F@{A, B, C, D} is equivalent to all of F@{A, B}, F@{A, C}, F@{A, D}, F@{B, C}, F@{B, D}, and F@{C, D} combined.

Greg Martin

Posted 2017-03-29T04:08:02.937

Reputation: 13 940

2

Python 2, 118 bytes

F=[]
def s(f,c):c=set(c);r=c in F;return F.append(c)if f%2 else not r^(4 in[len(x|c)for x in F])if f else 2>len(c)or r

Try it online!

Since I couldnt find repl online tool for python 2, I've added the TIO Nexus(in REPL format).

Query for option and its possible output

0 for Known - None

1 for Friends - True or False

2 for Suggest - True or False

Example of usage and sample output in an repl python interpreter.

>>> F=[]
>>> def s(f,c):c=set(c);r=c in F;return F.append(c)if f%2 else not r^(4 in[len(x|c)for x in F])if f else 2>len(c)or r
...
>>> s(1,['A','B'])
>>> s(1,['A','C'])
>>> s(1,['B','D'])
>>> s(2,['A','B'])
False
>>> s(2,['A','D'])
True
>>> s(2,['C','D'])
False
>>> s(0,['D','B'])
True
>>> s(0,['D','C'])
False

Keerthana Prabhakaran

Posted 2017-03-29T04:08:02.937

Reputation: 759

0

GNU sed, 158 + 2(rn flags) = 160 bytes

Since sed is a regex based language, there are no primitive types, not to mention abstract data structures. The network data is stored as free format text, in this case as redundant friend links like A-B;B-A;etc., which is then matched against various regex patterns.

G
/^F/{s:. (.+) (.+)\n:\1-\1;\1-\2;\2-\1;\2-\2;:;h}
/^K |^S /{s:(.) (.+) (.+)\n.*\2-\3.*:\1:;/^K$/p}
/^S /s:(.) (.+) (.+)\n.*(.+)-(\2.*\4-\3|\3.*\4-\2).*:\1:p

Try it online!

By design, sed runs the whole script for each input line. I recommend testing in interactive mode, to see the output of a command immediately after typing.

Usage: there are no truthy / falsy values in sed, so the output convention I use is borrowed from bash, whereby a non-empty string is considered as truthy, and an empty string as falsy.

  • F X Y for FRIEND X Y. It has no output.
  • K X Y for KNOW X Y. Outputs 'K' as truthy, and nothing as falsy.
  • S X Y for SUGGEST X Y. Outputs 'S' as truthy, and nothing as falsy.

Explanation:

G
# append stored network data, if any, to the current input line
/^F/{
# if command is 'F' (FRIEND), for ex. 'F X Y'
   s:. (.+) (.+)\n:\1-\1;\1-\2;\2-\1;\2-\2;:
   # generate friend links, for ex. 'X-X;X-Y;Y-X;Y-Y'
   h
   # store updated network data
}
/^K |^S /{
# if command is either 'K' (KNOW) or 'S' (SUGGEST), for ex. 'K X Y'
   s:(.) (.+) (.+)\n.*\2-\3.*:\1:
   # search friend link 'X-Y'. If found, delete pattern except the command letter.
   /^K$/p
   # if only letter K left, print it (command is 'K', 'X' and 'Y' are friends)
}
/^S /
# if command is 'S', for ex. 'S X Y', but 'X' and 'Y' aren't friends
   s:(.) (.+) (.+)\n.*(.+)-(\2.*\4-\3|\3.*\4-\2).*:\1:p
   # search if 'X' and 'Y' have a friend in common (for ex. 'C'), and if so print
   #letter S. The search is for ex. 'C-X.*C-Y' and 'C-Y.*C-X'.

seshoumara

Posted 2017-03-29T04:08:02.937

Reputation: 2 878