When using GKLocalPlayerListener's receivedTurnEventFor match, is there a way to determine if the turn event is the result of a matchmaking find? - ios

In iOS 9 Apple deprecated the public func turnBasedMatchmakerViewController(_ viewController: GKTurnBasedMatchmakerViewController, didFind match: GKTurnBasedMatch)
method of the GKTurnBasedMatchmakerViewControllerDelegate.
Apple's direction is to use the func player(_ player: GKPlayer, receivedTurnEventFor match: GKTurnBasedMatch, didBecomeActive: Bool) method of GKLocalPlayerListener.
Using only receivedTurnEventFor match leads to the following scenario:
User taps + which displays a GKTurnBasedMatchmakerViewController.
User taps Play Now and Game Center will search for a match.
Game Center will return a match with empty matchData (a new match), or place the user into a match in progress — and the game is informed of this through receivedTurnEventFor match.
While it is simple enough to determine if a new match has been created (using matchData), there doesn't appear to be a way to determine if a match has been found vs a match being updated as all events flow through receivedTurnEventFor match.
My question is this:
When using GKLocalPlayerListener's receivedTurnEventFor match method, is there a way to determine if the turn event is the result of a matchmaking find?

There are four cases:
1. You join a new match and it's your turn (you are creator of the match)
2. You join a new match and it's other player turn (you found the match)
3. You join an exising match and it's your turn
4. You join an existing match and it's other player turn.
Cases 3 and 4 could be when you switch between matches or rejoin a match.
You can check match.currentPatriticant and lastTurnDate properties to determine which case takes place.
If a player just create a new match (receivedTurnEventFor is called as a result of matchmaking) the match.participants[0].lastTurnDate is nil and match.currentParticipant?.player?.playerID is equal GKLocalPlayer.localPlayer().playerID (case 1).
If you join an existing match:
match.participants[0].lastTurnDate is nil and match.currentParticipant?.player?.playerID is NOT equal GKLocalPlayer.localPlayer().playerID (you joined a new match, but other player is in a turn - case 2)
Do determine unambiguously cases 3 and 4 you can check lastTurnDate of all participant and compare local player with current player.

I don't think game center will help you on this one. It looks like yet another ambiguous call to the overloaded receivedTurnEventForMatch function. But, I believe you can manage this yourself since you know the originating player always sees the match first, before any of the other participants.
Looks like there are four cases here:
The player creates a new match with at least 1 automatch participant: you can detect this because the participants will be in "matching" status when you receive the new match. In this case, as originator, you can set flags in the match data that indicates which players have invites and which are automatches.
The player creates a new match with invites: In this case, all of the participants should be set in the newly received match. Again, you can set flags in the match data that subsequent players can read.
The player joins a match that was created as an automatch: If you support only 2 players, at the time player2 receives this match both playerIDs will be set. If you support more, there's an (unreliable) chance that some slots will still be set to automatch, but that's not very helpful. If the originating player set flags in the match data indicating which players were invited vs automatch slots, though, this player can determine their own status.
The player joins a match had one or more invitations: Same as the prior situation. The joining player can't determine anything useful from game center, and will rely on data added to the match by the originating player.

Related

How to create random relationships between nodes?

I'm trying to create random transaction between bank accounts. I have created the following query:
//Create transactions
CALL apoc.periodic.iterate("
match (a:BANK_ACCOUNT)
WITH apoc.coll.randomItem(collect(a)) as sender
return sender", "
MATCH (b:BANK_ACCOUNT)
WHERE NOT sender = b
WITH apoc.coll.randomItem(collect(b)) as receiver
MERGE (sender)-[r:HAS_TRANSFERED {time: datetime()}]->(receiver)
set r.amount = rand()*1000",
{batchSize:100, parallel:false});
I would assume that it would create 100 random transactions between random bank accounts. Instead it creates 1 new bank account and 1 new relationship. What am I doing wrong and what should I do?
Thanks for your help !
The following query uses apoc.coll.randomItems to get 200 different random accounts at one time (which is much faster than getting one random account 200 times):
MATCH (ba:BankAccount)
WITH apoc.coll.randomItems(COLLECT(ba), 200) AS accts
WHERE SIZE(accts) > 1
UNWIND RANGE(0, SIZE(accts)/2*2-1, 2) AS i
WITH accts[i] AS sender, accts[i+1] AS receiver
CREATE (sender)-[:TRANSFERED_TO {time: datetime()}]->(receiver)
Notes:
This query uses CREATE instead of MERGE because it is unlikely that a TRANSFERED_TO relationship already exists with the current time as the time value. (You can choose to use MERGE anyway, if duplication is still possible.)
The WHERE SIZE(accts) > 1 test avoids errors when there are not at least 2 accounts.
SIZE(accts)/2*2-1 calculation prevents the RANGE function from generating a list index (i) that exceeds the last valid index for a sender account.

neo4j - Return single instance of node - querying by property?

I am building a social network that has a specialized audience.
Users are related to each other by three primary relationship types.
[:FRIENDS]->(:USER),
[:WORKS_AT]->(:COMPANY),
[:WORKED_AT]->(:COMPANY),
[:FOLLOWS].
When working through a search scenario (a user wants to find another user), I've given each relationship a "priority" (so to speak).
For example, if a user wants to find another user named "Bart Simpson" - first, we will check co-worker relationships ([:WORKS_AT],[:WORKED_AT]). I've assigned those relationships a priority of 1. That way, "Bart Simpson" who works with me will appear in the search results before "Bart Simpson" - who lives hundreds of miles away in Springfield.
The second priority is [:FRIENDS]->(:USER). Do any of my friends have a friend named "Bart Simpson?" Priority #2.
The last priority is a global search. I don't have any co-workers named "Bart Simpson", my friends don't have any friends named "Bart Simpson" - but I met Bart at a conference, and I want to "friend" him. So, I've added a "Global" search. Find any users named "Bart Simpson".
So far, this is my Cypher:
optional match (u:USER {id:'1'})-[:WORKS_AT|:WORKED_AT]-(w:COMPANY)-[r]-(f:USER)
with collect(f{.*, priority:1,relationship:r.title,type:type(r)}) as user
optional match (u:USER {id: '1'})-[:FRIENDS]-(:USER)-[r:FRIENDS]-(f:USER)
with user + collect(f{.*, priority:2,relationship:r.title,type:type(r)}) as user
optional match (f:USER)
where f.id <> '1'
with user + collect(f{.*, priority:3,relationship:'',type:''}) as user
unwind user as users
with users as user
where toLower(user.last_name) STARTS WITH toLower('Sc') OR toLower(user.first_name) STARTS WITH toLower('Sc')
return distinct user
This is fantastic - however, a user could work at the same company, as well as
be friends, as well as appear in the global search. So - we have the potential for three (or more) "copies" of the same user - with different relationship attributes. The relationship attributes are important because in the app, they provide important context to the search. "Bart Simpson - Works at XYZ Company."
So what I'm really looking for is the ability to either return the user record with the highest priority - and do that based on the "ID" field. If that doesn't work, I could see a situation where we try to update the property of a node. So, when the query hits the priority 2 search, if there is already a user in the collection with the same "ID", it just appends the P2 relationship type to the record. Either is fine with me.
I'm open to suggestions and listening!
So, I've made some progress!
MATCH
(subject:USER {id:'1'})
MATCH
(subject)-[:WORKS_AT|:WORKED_AT]-(w:COMPANY)-[r]-(f1:USER)
WHERE
toLower(f1.last_name) STARTS WITH toLower('Sc') or
toLower(f1.first_name) STARTS WITH toLower('Sc')
WITH
COLLECT(f1.id) AS userIds,
COLLECT(f1{.*,priority:1,rType:type(r), title:r.title, detail:w.name}) AS users
OPTIONAL MATCH
(subject)-[:FRIEND]-(fw:USER)-[r:FRIEND]-(f2:USER)
WHERE
NOT(f2.id in userIds) AND
(
toLower(f2.last_name) STARTS WITH toLower('Sc') or
toLower(f2.first_name) STARTS WITH toLower('Sc')
)
WITH
users + COLLECT(f2{.*,priority:2,rType:"FRIEND", title:"Friends with " + fw.first_name + " " + fw.last_name, detail:''}) AS users,
userIds + collect(f2.id) AS userIds
OPTIONAL MATCH
(f3:USER)
WHERE
NOT(f3.id in userIds) AND
(
toLower(f3.last_name) starts with toLower('Sc') OR
toLower(f3.first_name) starts with toLower('Sc')
)
WITH
users + COLLECT(f3{.*,priority:3,rType:"GLOBAL", title:"", detail:''}) AS users
RETURN
users
The query has evolved a bit. Essentially, at the first stage, we collect the userIds of the items that were returned. At each subsequent stage, the results returned are compared against the running list of ids. If the id of the result is already in the list of ids, it is filtered out - thus ensuring a unique id in the set.
This is working - and for now, I'm going to run with it. Is this the most efficient query, or is there a better way to deal with this scenario?

Turn-based matchmaking not working in iOS 10

My game was working fine before iOS 10. Now, everyone with iOS 10 can't invite and play with who they want.
When a user says they want to play multiplayer, I create a GKMatchRequest like this:
GKMatchRequest *request = [[GKMatchRequest alloc] init];
request.minPlayers = 2;
request.maxPlayers = 4;
request.defaultNumberOfPlayers = 2;
I use a GKTurnBasedMatchmakerViewController to handle the invitation, etc. The user sees an interface that would let them change the number of players in the match and invite players. Let's say that they only want 2 players, so they leave that and they want to play with their friend. So, they use the interface to send an invite to their friend. The result, is that didFindMatch is called on my delegate with a GKMatch that has 4 participants. It should only have 2! The first participant is the local player and the other 3 have the status "Matching". So, their friend isn't even in the list. Does anyone have any suggestions for fixing this? This same code works fine in versions of iOS before iOS 10.
It appears that with IOS10, the defaultNumberOfPlayers isn't being honored.
Keep in mind that GKTurnBasedMatch has three types of players in a given match:
The Originator who creates the match
Players that were specifically invited into the match
Players that automatch into the match
If you look at the player status for all 4 players in the match (using your example), I suspect you will see the following results
Active (the originator)
Invited (the 1 person you invited)
Matching
Matching
If so, that would indicate that defaultNumberOfPlayers was ignored, and the match was created with a max of 4 players (1 originator, 1 invitee and 2 automatch slots).
The workaround seems to be to set the maxNumberOfPlayers to the desired cap, in this case, 2, when you create the match.

Return all users given user chats with and the latest message in conversation

my relationships look like this
A-[:CHATS_WITH]->B - denotes that the user have sent at least 1 mesg to the other user
then messages
A-[:FROM]->message-[:SENT_TO]->B
and vice versa
B-[:FROM]->message-[:SENT_TO]->A
and so on
now i would like to select all users a given user chats with together with the latest message between the two.
for now i have managed to get all messages between two users with this query
MATCH (me:user)-[:CHATS_WITH]->(other:user) WHERE me.nick = 'bazo'
WITH me, other
MATCH me-[:FROM|:SENT_TO]-(m:message)-[:FROM|:SENT_TO]-other
RETURN other,m ORDER BY m.timestamp DESC
how can I return just the latest message for each conversation?
Taking what you already have do you just want to tag LIMIT 1 to the end of the query?
The preferential way in a graph store is to manually manage a linked list to model the interaction stream in which case you'd just select the head or tail of the list. This is because you are playing to the graphs strengths (traversal) rather than reading data out of every Message node.
EDIT - Last message to each distinct contact.
I think you'll have to collect all the messages into an ordered collection and then return the head, but this sounds like it get get very slow if you have many friends/messages.
MATCH (me:user)-[:CHATS_WITH]->(other:user) WHERE me.nick = 'bazo'
WITH me, other
MATCH me-[:FROM|:SENT_TO]-(m:message)-[:FROM|:SENT_TO]-other
WITH other, m
ORDER BY m.timestamp DESC
RETURN other, HEAD(COLLECT(m))
See: Neo Linked Lists and Neo Modelling a Newsfeed.

Get introduced [linkedin] like

I am using neo4j with people and companies as nodes and friend_of/works_at relationship between these.
I would like to know how to implement a get introduced to a second degree connection that linked in uses. The idea is to get your second degree connections at the company you wish to apply. If there are these second degree connections, then you would like to know who among your 1st deg connections can introduce y*ou to these 2nd deg connections.
For this I'm trying this query :
START from = node:Nodes(startNode), company = node:Nodes(endNode)
MATCH from-[:FRIEND_OF]->f-[:FRIEND_OF]-fof-[:WORKS_AT]->company
WHERE not(fof = from) and not (from-[:FRIEND_OF]->fof)
RETURN distinct f.name, fof.name, company.name
But, this returns duplicate friend of friend names (fof.name), since the distinct is applied on all the parameters that are returned as a whole. It could be like I have friends X and Y who are both connected to Z who works at company C. This way, I get both X-Z-C and Y-Z-C. But, I want to apply distinct on Z, such that I get either X-Z-C or Y-Z-C or maybe a list/collection/aggregate of all friends that connect to Z. This could like ["X","Y"..]->Z How should I modify my query?
http://console.neo4j.org/?id=s1m14g
start joe=node:node_auto_index(name = "Joe")
match joe-[:knows]->friend-[:knows]->friend_of_friend
where not(joe-[:knows]-friend_of_friend)
return collect(friend.name), friend_of_friend.name

Resources