How to implement turn-based game in Orleans? - orleans

I am trying to implement a simple turn based game when N players can join the game before the games begins. Once the game begins, in each round each player takes a turn to roll a pair of dice in order they joined the game. There are three rounds in the game. At the end of the game, each player is awarded points.
For example, assume players P1, P2, P3, P4 joined the game in that order, then in the same order they will go about rolling the pair of dice. After three rounds, assume the results are
P1: (3,5), (4,1), (2,3)
P2: (5,5), (4,3), (1,3)
P3: (3,3), (4,4), (2,1)
For game, I am thinking something like:
public interface IGameBase : IGrainWithGuidKey
{
Task<bool> IsActive();
Task Join(IPlayerBase player);
Task Leave(IPlayerBase player);
}
public interface IGame<TState, in TRequest> : IGameBase
{
Task<TState> State();
Task<TState> Play(IPlayerBase player, TRequest request);
}
public interface IPlayerBase : IGrainWithGuidCompoundKey
{
Task<IPlayerInfo> Info();
Task<IEnumerable<Guid>> Games();
Task Subscribe(IGameBase game);
Task Unsubscribe(IGameBase game);
}
How should I implement a way to let players know that it is their turn to play?

I was able to resolve this by implementing it using streams. Basically sent a play message to the stream with game id and player id, and the players listen to these messages. The one whose turn it is will send a play move to game grain.
The code is here https://github.com/sheeri185adidam/orleanedgames

Related

What is a better way than a for loop to implement an algorithm that involves sets?

I'm trying to create an algorithm along these lines:
-Create 8 participants
-Each participant has a set of interests
-Pair them with another participant with the least amount of interests
So what I've done so far is create 2 classes, the Participant and Interest, where the Interest is Hashable so that I can create a Set with it. I manually created 8 participants with different names and interests.
I've made an array of participants selected and I've used a basic for in loop to somewhat pair them together using the intersection() function of sets. Somehow my index always kicks out of range and I'm positive there's a better way of doing this, but it's just so messy and I don't know where to start.
for i in 0..<participantsSelected.count {
if participantsSelected[i].interest.intersection(participantsSelected[i+1].interest) == [] {
participantsSelected.remove(at: i)
participantsSelected.remove(at: i+1)
print (participantsSelected.count)
}
}
So my other issue is using a for loop for this specific algorithm seems a bit off too since what if they all have 1 similar interest, and it won't equal to [] / nil.
Basically the output I'm trying is to remove them from the participants selected array once they're paired up, and for them to be paired up they would have to be with another participant with the least amount of interests with each other.
EDIT: Updated code, here's my attempt to improve my algorithm logic
for participant in 0..<participantsSelected {
var maxInterestIndex = 10
var currentIndex = 1
for _ in 0..<participantsSelected {
print (participant)
print (currentIndex)
let score = participantsAvailable[participant].interest.intersection(participantsAvailable[currentIndex].interest)
print ("testing score, \(score.count)")
if score.count < maxInterestIndex {
maxInterestIndex = score.count
print ("test, \(maxInterestIndex)")
} else {
pairsCreated.append(participantsAvailable[participant])
pairsCreated.append(participantsAvailable[currentIndex])
break
// participantsAvailable.remove(at: participant)
// participantsAvailable.remove(at: pairing)
}
currentIndex = currentIndex + 1
}
}
for i in 0..<pairsCreated.count {
print (pairsCreated[i].name)
}
Here is a solution in the case that what you are looking for is to pair your participants (all of them) optimally regarding your criteria:
Then the way to go is by finding a perfect matching in a participants graph.
Create a graph with n vertices, n being the number of participants. We can denote by u_p the vertex corresponding to participant p.
Then, create weighted edges as follows:
For each couple of participants p, q (p != q), create the edge (u_p, u_q), and weight it with the number of interests these two participants have in common.
Then, run a minimum weight perfect matching algorithm on your graph, and the job is done. You will obtain an optimal result (meaning the best possible, or one among the best possible matchings) in polynomial time.
Minimum weight perfect matching algorithm: The problem is strictly equivalent to the maximum weight matching algorithm. Find the edge of maximum weight (let's denote by C its weight). Then replace the weight w of each edge by C-w, and run a maximum weight matching algorithm on the resulting graph.
I would suggest that yoy use Edmond's blossom algorithm to find a perfect matching in your graph. First because it is efficient and well documented, second because I believe you can find implementations in most existing languages, but also because it truly is a very, very beautiful algorithm (it ain't called blossom for nothing).
Another possibility, if you are sure that your number of participants will be small (you mention 8), you can also go for a brute-force algorithm, meaning to test all possible ways to pair participants.
Then the algorithm would look like:
find_best_matching(participants, interests, pairs):
if all participants have been paired:
return sum(cost(p1, p2) for p1, p2 in pairs), pairs // cost(p1, p2) = number of interests in common
else:
p = some participant which is not paired yet
best_sum = + infinite
best_pairs = empty_set
for all participants q which have not been paired, q != p:
add (p, q) to pairs
current_sum, current_pairs = find_best_matching(participants, interests, pairs)
if current_sum < best_sum:
best_sum = current_sum
best_pairs = current_pairs
remove (p, q) from pairs
return best_sum, best_pairs
Call it initially with find_best_matching(participants, interests, empty_set_of_pairs) to get the best way to match your participants.

How to get the last nth record for each group in a rails query

I have a table named Game which is filled with individual players statlines from all their games in an nhl season. The fields That I am concerned with here are games_played which = n for the nth game of a player in the season.
and name which is the players name.
Essentially I am trying to figure out how to grab the nth last game for each player.
For instance I know the following query can get me the last game for each player
Game.group(:name).having('games_played = MAX(games_played)')
However when I try the following (lets say n is 10)
Game.group(:name).having('games_played=MAX(games_played)-10')
I get no results
and in fact if I explicitly do something like
Game.group(:name).having('games_played=16')
I only get the last game of players who played exactly 16 games, instead of the 16th game for all players. (Which I guess explains the no results of the previous query)
How do I go about getting the nth last game for each player?
Is this even the right way to query this? I also have a player table where a player has_many games, and a game belongs_to a player. Should I be taking advantage of that?
To find the nth last game for a specific player, it would probably be easiest to start by finding the player.
player = Player.find_by(name: "user3692508")
then you can find the players games with:
player.games
To get the nth last game, you can order it by games_played in descending order, then limit it to one result and offset it with the offset you want:
player.games.order(games_played: :desc).limit(1).offset(0)
If you do an offset of 0, you will get the last game. If you do an offset of 1 you will get the 2nd last game and so on.
This is assuming your player has_many :games and the game belongs_to :player
By using a sub query you can get the nth last game for each player.(it looks like a mess.....)
offset = 1
sub_query = "SELECT 'sub_game'.id FROM 'games' as sub_game WHERE 'sub_game'.'player_id' = games.player_id ORDER BY 'sub_game'.'games_played' DESC LIMIT 1 OFFSET #{offset}"
Game.joins(:player).where("games.id IN (#{sub_query})").order(id: :desc).group(:player_id)
With this solution, you would sort the games for each player in the sub query and do the offset there first.

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

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.

How can I tell if I'm the one who disconnected first in a GC match?

My iOS game has Game Center multiplayer matches. They are 1v1 battles.
If a player disconnects (that is, their state changes to GKPlayerStateDisconnected), such player loses the duel automatically (even if they manage to reconnect later somehow).
Suppose that players A (ID: 123) and B (ID: 456) are dueling. Then, player B loses their internet connection.
Both players receive the callback:
-(void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
Player A, who is still connected to the match, gets that playerID is 456. This is indeed right because it was player B who disconnected.
Player B, on the other hand, gets that playerID is 123! Why? Shouldn't it be 456 since he himself is the one who disconnected first?
Anyway, this leads to my main problem: how am I suppose to tell who disconnected first from the match?
I need Player B to realize that he's the one who disconnected, so that the player receives a prompt saying "you lost the match because you disconnected".

JUNG: Custom DijkstraShortestPath

I discovered this lib this week, and my first project works, I modelize simple flight booking.
As edge I have created an Flight Class
As vertex I have created an Airport Class
I put duration for each flight and succeed to associate dijsktra algorithm (DijkstraShortestPath)
class Airport {
String name;
}
class Flight {
String flight;
int duration;
}
g = new DirectedSparseMultigraph<Airport, Flight>();
Flight AXXX = new Flight("A57",3);
Flight AYYY = new Flight("A53",1);
ORY = new Airport("ORY");
LYS = new Airport("LYS");
g.addEdge(AXXX, ORY, LYS);
g.addEdge(AYYY, LYS, ORY);
Transformer<Flight, Integer> wtTransformer = new Transformer<Flight, Integer>() {
#Override
public Integer transform(Flight link) {
return link.duration;
}
};
DijkstraShortestPath<Airport, Flight> alg = new DijkstraShortestPath(g, wtTransformer);
Number dist = alg.getDistance(ORY, LYS);
This simple case works well, but now I would to calculate duration as:
Flight1 start 12/01/13 at 12:00 and arrival at 13/01/13 at 14h
Flight2 start 13/01/13 at 18:00 and arrival at 13/01/13 at 20h
In this case I want to calculate duration of flight AND between flight. Cause to get shortest path from one to another flight we need to take care about time to wait between flight and not only flight duration.
But DiskstraShortestPath allow only Transformer as: Transformer so I can’t get reference to previous flight to calculate total duration (wait + flight).
So my question is: what is the best way for my case ?
Create new algorithm (inherit DijsktraShortestPath…)
Create new GraphType (inherit DirectedSparseMultigraph…)
Thanks for you answer guy ;) !
If you are trying to minimize total travel time, then this is, indeed, not a shortest path problem, but a different kind of discrete optimization problem. JUNG does not provide a general solver for discrete optimization problems.
Even if you're trying to minimize flight time only (that is, time spent in the air) then you need to be able to filter the graph (more precisely, the outgoing edges) at each step, because only flights that depart after the previous flight arrives are relevant, i.e., the local topology is a function of time.

Resources