How to get high scores from OpenFeint? - iphone

In their support OpenFeint give you this, but I don't quite understand. How can I get the leaderboard data, say top 10 and show it in my own UI?
Original link: http://www.openfeint.com/ofdeveloper/index.php/kb/article/000028
[OFHighScoreService getPage:1 forLeaderboard:#"leaderboard_id_string" friendsOnly:NO silently:YES onSuccess:OFDelegate(self, #selector(_scoresDownloaded:)) onFailure:OFDelegate(self, #selector(_failedDownloadingScores))];
- (void)_scoresDownloaded:(OFPaginatedSeries*)page
{
NSMutableArray* highscores = nil;
if ([page count] > 0)
{
if ([[page objectAtIndex:0] isKindOfClass:[OFTableSectionDescription class]])
{
// NOTE: In the following line, we access "[page objectAtIndex:1]" to retrieve high scores from
// the global leaderboard. Using "[page objectAtIndex:0]" would retrieve scores just for the local player.
// Older versions of OpenFeint did not break this out into 2 sections.
highscores = [(OFTableSectionDescription*)[page objectAtIndex:1] page].objects;
}
else
{
highscores = page.objects;
}
}
for (OFHighScore* score in highscores)
{
// ...
}
}
- (BOOL)canReceiveCallbacksNow
{
return YES;
}

The code to request a page of high scores is the first line, i.e.:
[OFHighScoreService getPage:1 forLeaderboard:#"leaderboard_id_string" friendsOnly:NO silently:YES onSuccess:OFDelegate(self, #selector(_scoresDownloaded:)) onFailure:OFDelegate(self, #selector(_failedDownloadingScores))];
You put this line in the place where you want to start the query for high scores. You can change the page number as required. Once the page of high scores has been retrieved, the callback _scoresDownloaded is called. The example shows you how you would iterate through the OFHighScore objects in the highscores array. You would replace the comment // ... with your own code to show the scores to the player, or whatever.
(In case of error _failedDownloadingScores is called; you should implement that to show an error.)

Related

Determining the player who got connected in first place

In the game I'm working on I would like to give users the possibility to select between asynchronous and real-time turn-based matches. The one thing I'm missing from the latter is how do I know who's got the first turn. I'm trying to find a way to detect who's got connected first and set that player's turn automatically. Unfortunately, it seems that both players get connected at the same time since right after finding a match expectedPlayersCount yields 1 for both players and in the didChangeState event that same variable yields 0 for both of them too. So I have no way to tell who's got first to the match since it seems that it all happens simultaneously.
As a temporary fix, I'm just using their IDs. The one with the lowest ID is the one having the first turn. However, I'm trying to find a better way since with this approach player A will always get the first turn when playing against player B and that represents a small advantage in my game.
This method is what I call when starting a new game. I pass a realTime bool value to specify whether the game returned by the game center wrapper should be a GKMatch or GKTurnBasedMatch.
//Calculate the level group to find other players in the same group
NSInteger levelGroup = (self.player.levelValue / STLevelSkillInterval) * STLevelSkillInterval;
//Find a match for the local player
[self.gameCenter findMatchWithPlayerAttributes:levelGroup realTime:realTime group:0 onCompletion:^(GKTurnBasedMatch *turnMatch, GKMatch *realTimeMatch) {
[self handleTurnMatchFound:turnMatch realTimeMatch:realTimeMatch completionBlock:onCompletion];
}];
...
This method here is the responsible to handle game center response after finding match. The create and synchronize method stores a match instance in CoreData and also fills its data by fetching the corresponding info from Game Center and my backend. So if at the time of that's done the expected player count reached 0, I call the completion block immediately since the match can begin. Otherwise I just store the completion block so I can use it later when the other player connects to the match. The problem in that part is that it never reaches 0, not for any of the two players.
- (void)handleTurnMatchFound:(GKTurnBasedMatch *)turnMatch realTimeMatch:(GKMatch *)realTimeMatch completionBlock:(void(^)(STMatch *match, NSError *error))onCompletion
{
if (turnMatch != nil)
{
[self createAndSynchronizeLocalMatch:turnMatch onCompletion:^(STMatch *localMatch) {
onCompletion(localMatch, nil);
}];
}
else if (realTimeMatch != nil)
{
[self createAndSynchronizeRealTimeLocalMatch:realTimeMatch onCompletion:^(STMatch *localMatch) {
if (realTimeMatch.expectedPlayerCount == 0)
{
onCompletion(localMatch, nil);
}
else
{
self.findRealTimeMatchCompletionBlock = onCompletion;
}
}];
}
else
{
onCompletion(nil, nil);
}
}
...
This is the method that handles player's state changes. So basically here I just update the local instance of the real time match with values from the connected player and then I call the completion block stored earlier.
- (void)match:(GKMatch *)match player:(NSString *)playerID didChangeState:(GKPlayerConnectionState)state
{
if (state == GKPlayerStateConnected)
{
STMatchParticipant *placeholderParticipant = [self.realTimeMatch getMatchParticipantWithPlayerId:nil];
placeholderParticipant.playerId = playerID;
placeholderParticipant.statusValue = GKTurnBasedParticipantStatusActive;
//This will sync the information for the connected player only
[self syncAllPlayerInfoForRealTimeMatchOnCompletion:^(NSArray *players) {
self.findRealTimeMatchCompletionBlock(self.realTimeMatch, nil);
}];
}
else
{
//Notify the observers that the participant has disconnected from the match
STMatchParticipant *participant = [self.realTimeMatch getMatchParticipantWithPlayerId:playerID];
for (id<STTurnBasedMatchDelegate> observer in self.matchesObservers)
{
if ([observer respondsToSelector:#selector(realTimeMatch:participantDidDisconnect:)])
{
[observer realTimeMatch:self.realTimeMatch participantDidDisconnect:participant];
}
}
}
}
I would appreciate any comments.
Instead of trying to determine who got connected first, why don't you just have all the players pick a random number using arc4random ?
int randomNumber = arc4random%1000000;
The player with the biggest number can play first. You will have to make sure all the players send each other their random numbers so everyone can compare and decide who is first.
In the above example, the range would be upto 1 million, so the odds of two players picking the same random number is low.
If two players do pick the same random number, you could compute the hash of their playerId's, and have the player with the larger hash be first.
if ([GKLocalPlayer localPlayer].playerID.hash > otherPlayerId.hash )
The chances of a hash collision occurring are very low, since the playerId strings are short. You can still check for this collision and handle it appropriately (maybe by hashing again ;) ).

iOS - Amazon S3 Slow Download

I am creating an iOS app that allows users to write lets say a status update and people can comment on it, like it, interact with it in multiple ways, basically the status update has multiple properties that would be stored with it. Imagine an app with a home-screen of more than 50 of these status updates represented into a table view.
Now take your eyes away and focus on a practice/demo app, a developer trying to master the techniques before the big game (thats me!) So essentially, I have started out by setting up an S3 bucket based in Singapore. I live in Singapore, so there is a server nearby and really everything should be fast and smooth. Except, its just not. Its slow, and its starting to make me annoyed.
I know an app that uses S3 that loads high-definition panorama images with comments, likes etc. and it takes a second or a bit more for all this data to load. I am not sure how they actually carry out the process, I know they store the images on S3 but thats all I know. In my starter demo, I simply upload some pieces of text (say 20) then download them and it takes like 15 seconds under my 60 mbps wifi! These pieces of text don't even exceed a sentence each, they are phrases, so I am really kind of confused here.
I have CloudFront setup but isn't it for websites? I have the manage distributions and URL things all setup but how to setup in my code? This is probably my biggest question to nail down for release later in my other app. Even so, I live in Singapore and the bucket is in the Singapore server, so CloudFront for self-testing / practicing wouldn't be mandatory.
I find this extremely confusing, here is some annotated code I have produced, any problems, misconceptions that is leading it to be slow?
- (void)loadObjects {
S3ListObjectsRequest *listObjectRequest = [[S3ListObjectsRequest alloc] initWithName: #"testbucketquotes.rohanprostudios"];
S3ListObjectsResponse *listObjectResponse = [[AmazonClientManager s3] listObjects:listObjectRequest];
if(listObjectResponse.error != nil)
{
}
else
{
S3ListObjectsResult *listObjectsResults = listObjectResponse.listObjectsResult;
if (objects == nil) {
objects = [[NSMutableArray alloc] initWithCapacity:[listObjectsResults.objectSummaries count]];
}
else {
[objects removeAllObjects];
}
// By defrault, listObjects will only return 1000 keys
// This code will fetch all objects in bucket.
NSString *lastKey = #"";
for (S3ObjectSummary *objectSummary in listObjectsResults.objectSummaries) {
if ([[objectSummary key] rangeOfString: #"UploadedQuote"].location != NSNotFound) {
[objects addObject:[objectSummary key]];
lastKey = [objectSummary key];
}
}
while (listObjectsResults.isTruncated) {
listObjectRequest = [[S3ListObjectsRequest alloc] initWithName: #"testbucketquotes.rohanprostudios"];
listObjectRequest.marker = lastKey;
listObjectResponse = [[AmazonClientManager s3] listObjects:listObjectRequest];
if(listObjectResponse.error != nil)
{
break;
}
listObjectsResults = listObjectResponse.listObjectsResult;
for (S3ObjectSummary *objectSummary in listObjectsResults.objectSummaries) {
if ([[objectSummary key] rangeOfString: #"UploadedQuote"].location != NSNotFound) {
[objects addObject:[objectSummary key]];
}
lastKey = [objectSummary key];
}
}
if (objects.count) {
for (int i = 0; i <= objects.count - 1; i++) {
S3GetObjectRequest *req = [[S3GetObjectRequest alloc] initWithKey:[objects objectAtIndex: i] withBucket: #"testbucketquotes.rohanprostudios"];
`// asynchronously loads text (adds to operation queue)`
AsyncQuoteDownloader *quote = [[AsyncQuoteDownloader alloc] initWithRequest:req andViewController: self];
[operationQueue addOperation: quote];
// in 'AsyncQuoteDownloader' when finished calls a method in this view controller adding the response to an array and reloading a table
}
}
}
});
}
Anything wrong with my code that is making it lag so much? I would have thought this would take a matter of milliseconds if an imaging service would take 1 second to load HQ images with likes and comments etc takes 1-2 seconds.
Thank you for any help...
Update
Ok, so the iteration of keys doesn't seem to be the problem here but rather the downloading of the objects. Any thoughts? Thanks...
This is the first time I've ever even seen Objective C, so I may be completely wrong here. But... it looks like you're iterating through the keys of the entire bucket. This is going to be really, really slow with any appreciable quantity of keys.
A better design would be store a lookup table in something like DynamoDB (since you're already using AWS). You'd query there, get an array of matching id's (S3 keys), and then fetch the match objects from S3.
Another option, as I used in my own iOS app, is to use Heroku as the app layer and create a POSTGRESQL record that points to your content. This way you can create rich queries in rails and mediate upload and download with the power of Rails, rather then paying for both DynamoDB and S3.
Turns out my bucket was set to US region and not Singapore region... #doh
Its faster now, its working fine
I just needed to set the AmazonS3Client's endpoint to Singapore (SEA) region

iPhone - intersection of rectangles

I have this NSMutableArray which is a collection of objects that are being moved on the screen. When an object intersects another one, I need to construct an array with this object intersected. If this object is by itself intersecting with another, this one must be included in that array and so on, recursively until I know all the objects intersecting with the object that was intersecting with the other and so on.
Example: I am moving object1 and I intersect object2, but object2 intersects object3 that intersects 4, that intersects 5 and so on.
I want to collect all these objects in one array.
What I did was this:
NSMutableArray *intersectingObjects = [NSMutableArray array];
for (Obj *oneObj in allObjects) {
if (oneObj != movingObject) {
if (CGRectIntersectsRect(movingObject.frame, oneObj)) {
[intersectingObjects addObject:oneObj];
}
}
}
// at this point I got an array of all objects intersecting with the
// moving object, then I created a similar block to
// test all these intersecting objects against all objects again,
// then I discovered the objects that were intersecting with the first block
The problem is this just gives me 2 levels deep.
How do I create a recursion here, that will go to the whole tree of possibilities?
thanks.
Because the 1 time calculation would be on the order of O(n^2), I would suggest maintaining an NSMutableArray for each object which contains the objects it is currently intersecting directly. Then the order for each new calculation changes to O(n), simply taking a union of the items in the tree.
However, if you'd still like to pursue the O(n^2) method, here's an example. I'm assuming Obj is a subclass of UIView?
- (void) addViewsWhichIntersectView:(Obj*)movingObject toArray:(NSMutableArray*) intersectingObjects
{
for (Obj *oneObj in allObjects)
{
if (movingObject != oneObj && //assuming you only care about address comparison, override isEqual and use that method otherwise
![intersectingObjects containsObject:oneObj) &&
CGRectIntersectsRect(movingObject.frame, oneObj.frame)
{
[intersectingObjects addObject:oneObj];
[self addViewsWhichIntersectView: oneObj toArray:intersectingObjects];
}
}
}
Then for the driver, just initialize a mutable array and pass in your reference to the original object.
[self intersectingObjects:allObjects withObject:movingObject];
- (NSMutableArray*) intersectingObjects:(NSArray*)objects withObject:(id)obj{
NSMutableArray * objectsToCheck = [NSMutableArray arrayWithArray:objects];
[objectsToCheck removeObject:obj];
NSMutableArray * intersectingWith = [NSMutableArray array];
for (id oneStepObj in objectsToCheck) {
if (CGRectIntersectsRect(obj.frame, oneStepObj)) {
//This object intersected with the provided object
[intersectingWith addObject:oneStepObj];
//Also add all the objects that intersect with oneStepObj, take care of duplicates
for(id nStepObj in [self intersectingObjects:objectsToCheck withObject:oneStepObj]){
if(![intersectingWith containsObject:nStepObj]){
[intersectingWith addObject:nStepObj];
}
}
}
}
}
return intersectingWith;
}
Here is a N^2 approach (good for small N):
*intersectingObjects = [NSMutableArray array];
for (Obj *oneObj in allObjects) {
for (Obj* twoObj in allObjects) {
if ( oneObj != twoObj ) {
if (CGRectIntersectsRect(movingObject.frame, oneObj)) {
[intersectingObjects addObject:oneObj];
}
}
}
}
To be faster, you'd have to do some indexing of some sort. Recursion isn't necessarily better here unless you have a data structure of objects indexed by location. But it takes work to maintain that index (typically when you update locations).
I wrote an app that does this pretty well, it is called QColor - send me a request for a promo code if you want to see it.
In my experience, the iPhone stopped updating live with an inefficient algorithm. Here is what I settled on (pseudocode - sorry, the full source has a lot of other stuff going on).
One thing to note, this algorithm keeps multiple overlapping rectangles so when you update the screen you need to bringSubviewToFront: on the intersections with the most rectangles.
NSArray intersections; // each Intersection is an array of rectangles with a calculated rect - contact me if you want code that can do this (it's not glorious).
- (void) addRect: newRect {
intersections addObject: newRect;
for (Intersection *intersection in intersections) {
if (intersection intersects newRect) {
create new intersection of intersection + newRect
// note, do NOT modify the intersection - add a NEW one. Important point.
}
}
}
- (void) removeRect: aRect {
remove every intersection that contains aRect - careful, don't use fast enumeration while editing the data structure.
}
- (void) moveRect: aRect {
for (Intersection *intersection in intersections) {
if (intersection contains aRect) {
recompute the intersection with the moved aRect. If ANY rectangle no longer intersects, delete the entire intersection (compare count before and after recalculation)
} else {
if (intersection intersects newRect) {
create new intersection of intersection + newRect
}
}
}
}
This is not as pretty as a recursive algorithm, but it's important to keep the total number of intersections low. Now I first tried this with an n! algorithm so of course it choked. The n^2 one above, I'm not sure if it will be adequate. As I understand my algorithm, each time through is order n though some worst case examples with everything overlapping (but not completely) could be n! (that's the data, not the algorithm).
I have some screenshots on my app page if you want to visualize the rectangles: http://itunes.apple.com/ph/app/qcolor/id490077718?mt=8
Gotta run - sorry for any typos!
Damien

Facebook graph api data inconsistent application data IOS

In my app i'm using a users feeds to retrieve al the post done by my app. I simply retrieve al the posts, and the compare on every post the id number of the app.
This work ok. But i've found a bug in this method. Since the application node isn't always consistent. Normally when there is a post which is not done by an app, the entry in the dictionary just says (null), there isn't any data. This doesn't give any problems.
But there is an app which has other data in the this application node. This one has data in this node which specifically says (note the difference between () and <> ). But I can't seem any way to check if the dictionary with that post has in it. i've tried the following:
NSDictionary *resultPost1 =[resultPost objectForKey:#"application"];
NSLog(#"result%#", [resultPost objectForKey:#"application"]);
if ([resultPost1 count] != 0) {
This one gives a sigabrt, with the following nslog before the sigabrt:
result(null)
result{
id = 1957711133323244365557378;
name = "app";
}
result< null > (added space for visibility)
I've also tried isEqualtoString:#"< null>" Also without success.
It looks like sometimes, their is an dictionary in the application node, and sometimes a string .
Anyone has clue??? Thanks!!!
You will have to do some checking as you don't have a guarantee as to what sort of object is returned from the dictionary.
NSDictionary *resultPost1 = [resultPost objectForKey:#"application"];
if ([[resultPost1 class] isKindOfClass:[NSDictionary class]) {
//Treat as a dictionary
}
else if ([[resultPost1 class] isKindOfClass:[NSString class]) {
//Treat as a string
}
else if ([resultPost1 isEqual:[NSNull null] || !resultPost) {
//Treat as Null, note the json library Facebook uses might set
//a json NULL into a NSNull object instead of nil
}

Building a simple guessing game, iPhone

I'm trying to build a simple guessing game. I'm having a weird problem:
-(int)setRandom {
randomNum=(int)arc4random() % 100;
return randomNum;
}
-(IBAction)submit
{
num=[self setRandom];
if([numberField.text intValue] > num)
randomNumLabel.text=#"Too high. Try again";
else if([numberField.text intValue] < num)
randomNumLabel.text=#"Too low. Try again";
else
randomNumLabel.text=#"You got it, congrats!";
}
The problem is that I get a new random number every time I press submit. I thought that the first method would create the random number, and it would be the same every time, but apparently not. How do I fix this?
Don't call setRandom every time in submit. arc4random() returns a new random number every time you call it.
Create a property to store the random number and set it only when you need to -- in init and when the game resets.
For this purpose you need a global variable.so first a fall declare
NSInteger num; in .h class then in viewDidLoad write like this
- (void)viewDidLoad {
num=[self setRandom];
//you stuff
}
then in submit
-(IBAction)submit
{
if([numberField.text intValue] > num)
randomNumLabel.text=#"Too high. Try again";
else if([numberField.text intValue] < num)
randomNumLabel.text=#"Too low. Try again";
else
{
randomNumLabel.text=#"You got it, congrats!";
num=[self setRandom];
}
}
-(int)setRandom {
randomNum=(int)arc4random() % 100;
return randomNum;
}
Here you get a random number at view load then you get new random number when your answer matches with the number.
So this will help you.