PDA

View Full Version : Small Obje-C test program help




larswik
Jun 17, 2011, 06:34 PM
With what I know so far I am trying to just make a small program. A game called War with 10 cards (integers). The goal is to mix 10 cards into an array. Then I pull a card and then the computer. I am putting the cards into it's own class. I am compiling as I go to trouble shoot. The goal is to apply what I am learning.

Main:
#import <Foundation/Foundation.h>
#import "cards.h"

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

NSLog(@"***** Welcome to WAR! ******");
NSLog(@"Mixing the deck");
cards * gameCards = [[cards alloc]init];

[gameCards mixTenCards];


[pool drain];
return 0;
}
Interface:
#import <Cocoa/Cocoa.h>
#include <stdlib.h>

@interface cards : NSObject {
int cards[10]; //Card Array

}
-(void) mixTenCards;
-(int) cards;
@end
Implementation:
#import "cards.h"


@implementation cards

-(void) mixTenCards {
for (int i = 0; i <= 9; i++){
cards[i] = arc4random() % 51 + 1;
}
}
-(int) cards{
return cards;
}

@end

When I compile I get an error with this line, and another but this one first.
cards[i] = arc4random() % 51 + 1;
It says it 'Expected and identifier or ( before [ token' But I already identified this as in INT in the interface?



kainjow
Jun 17, 2011, 06:39 PM
"cards" is the name of your class, you cannot reuse it as a variable.

Maybe rename your "cards" class to "Game"?

holmesf
Jun 17, 2011, 06:43 PM
Hello,

The compiler is probably confused because your class name is "cards", but your class also has an instance variable called "cards". Try changing the class name to Cards instead (it is the standard Cocoa coding convention that class names should be capitalized anyway).

chown33
Jun 17, 2011, 07:31 PM
Coding Guidelines for Cocoa (http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html)

larswik
Jun 18, 2011, 01:48 AM
HAHA. I knew that and I still made that mistake.

-Lars

larswik
Jun 18, 2011, 03:06 AM
I finished the little WAR app. After you guys brought to my attention I had a var the same name as the class, it was some what easy to finish. I am going to post it to see if you guys have any comments about the code structure. Like, I should have made more Objects to handle the routines, or any glaring errors that I need to keep in mind for the future as I learn to make my code more readable.

Main:
#import <Foundation/Foundation.h>
#import "cards.h"

int Computer, Player, pHigh, cHigh;

int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

Computer = 0;
Player = 0;
pHigh = 0;
cHigh = 0;

NSLog(@"***** Welcome to WAR! ******");
NSLog(@"Mixing the deck");

cards * gameCards = [[cards alloc]init];
[gameCards mixTenCards];
[gameCards setNum: 0];

for (int i = 0; i <= 9; i++) {
[gameCards setNum: i];
pHigh = [gameCards deck];
i++;
[gameCards setNum: i];
cHigh = [gameCards deck];

if (pHigh == cHigh) {
Player++;
Computer++;
}
else if (pHigh < cHigh)
Computer++;

else {
Player++;
}
}
NSLog(@"Player had: %i", Player);
NSLog(@"Computer had: %i", Computer);

[gameCards release];
[pool drain];
return 0;
}

Inheritance:
#import <Cocoa/Cocoa.h>
#include <stdlib.h>

@interface cards : NSObject {
int deck[10]; // Card Array
int num;

}
-(void) mixTenCards;
-(int) deck;
-(void) setNum: (int) a;
-(int) num;
@end
Implementation:
#import "cards.h"

@implementation cards

-(void) mixTenCards {
for (int i = 0; i <= 9; i++){
deck[i] = arc4random() % 12 + 1;
}
}
-(int) deck{
return deck[num];
}
-(void) setNum: (int) a{
num = a;
}
-(int) num{
return num;
}
@end

Thanks again for all your help.

-Lars

jiminaus
Jun 18, 2011, 04:53 AM
Hi Lars,

One of the things architecture students do is to study the design of existing buildings. So what I've done is refactor your program, for your study.

BTW I'm not happy with using ints to represent cards. I want to create a WarCard class with a value instance variable, and then have WarDesk's theCards be an array of WarCard objects. But I resisted the instinct, because you might not have encountered arrays of objects or NSArray.

Jim

main.m

#import <Foundation/Foundation.h>
#import "WarGame.h"

int main(int argc, const char* argv[])
{
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];

NSLog(@"***** Welcome to WAR! ******");

WarGame* game = [[WarGame alloc] init];
[game playThroughDeck];

NSLog(@"Player had: %lu", [game playersScore]);
NSLog(@"Computer had: %lu", [game computersScore]);

[game release]

[pool drain]

return 0;
}


WarGame.h


#import <Foundation/Foundation.h>

@class WarDeck;

@interface WarGame : NSObject
{
WarDeck* theDeck;
NSUInteger theComputersScore;
NSUInteger thePlayersScore;
}

- (NSUInteger)computersScore;
- (NSUInteger)playersScore;

- (void)playThroughDeck;

@end


WarGame.m

#import "WarGame.h"
#import "WarDeck.h"

@implementation WarGame

- (id)init
{
self = [super init];
if (self) {
theDeck = [[WarDeck alloc] init];
theComputersScore = 0;
thePlayersScore = 0;
}
return self;
}

- (void)dealloc
{
[theDeck release];
[super dealloc];
}

- (NSUInteger)computersScore
{
return theComputersScore;
}

- (NSUInteger)playersScore
{
return thePlayersScore;
}

- (void)playThroughDeck
{
for (;;) {
int playersCard = [theDeck drawNextCard];
if (playersCard == kNoCard) {
break;
}

int computersCard = [theDeck drawNextCard];
if (computersCard == kNoCard) {
break;
}

if (playersCard <= computersCard) {
theComputersScore++;
}
if (playersCard >= computersCard) {
thePlayersScore++;
}
}
}

@end

(My use of for(;;) and break in playThroughDeck is controversial).

WarDeck.h

#import <Foundation/Foundation.h>

extern const int kNoCard;

@interface WarDeck : NSObject
{
int theCards[10];
NSUInteger theIndexOfTheNextCard;
}

- (id)init;
- (int)drawNextCard;

@end


WarDeck.m

#import "WarDeck.h"

const int kNoCard = -1;

@implementation WarDeck

- (id)init
{
self = [super init];
if (self) {
for (NSUInteger i = 0; i < 10; i++) {
theCards[i] = arc4random() % 12 + 1;
theIndexOfTheNextCard = 0;
}
}
return self;
}

- (int)drawNextCard
{
if (theIndexOfTheNextCard >= 10) {
return kNoCard;
} else {
int nextCard = theCards[theIndexOfTheNextCard];
theIndexOfTheNextCard++;
return nextCard;
}
}

@end

balamw
Jun 18, 2011, 01:44 PM
So what I've done is refactor your program, for your study.


One thing Jim's refactoring does, that I am a big fan of, is separate out the UI bits (the NSLog statements) from the game "engine".

You can see how this would be easier to transition to Cocoa or Cocoa Touch as a result of this refactoring.

B

larswik
Jun 19, 2011, 01:02 AM
WOW Jim... Thanks for that rewrite!

I was not expecting that an answer would be so detailed. Some of it is confusing and somethings I learned.

I first was confused about the classes. I thought the implementation section of a class held the methods for the corresponding interface of that class. In your warGame class you had a couple of methods that were not listed in the header file? I thought your .h file is kind of like a packing list you receive when you get a box in the mail. In this case there were extra things in the box so to speak of that were not on the list, '- (void)dealloc' for instance.

I then saw only 1 -(id)init declaration in the warDeck.h but a method definition was located in both of the classes? I would have expected to see a decoration for this in both .h classes?

I looked up this on line (NSUInteger). I guess this is the 64 bit version of an int, or unsigned int.

In warDeck.h I see ' WarDeck* theDeck;' in the section for defining variables. You then alloc and init this object in the '-(id)init' Method, I understand this part. The return type is a generic object id and if it successfully creates it's self, then that object will contain a 10 integer array, theCards of random numbers, and theIndexOfTheNextCard variable. Is that conclusion right?

Thanks again!!!

-Lars

3)

jiminaus
Jun 19, 2011, 03:12 AM
I first was confused about the classes. I thought the implementation section of a class held the methods for the corresponding interface of that class. In your warGame class you had a couple of methods that were not listed in the header file? I thought your .h file is kind of like a packing list you receive when you get a box in the mail. In this case there were extra things in the box so to speak of that were not on the list, '- (void)dealloc' for instance.

I then saw only 1 -(id)init declaration in the warDeck.h but a method definition was located in both of the classes? I would have expected to see a decoration for this in both .h classes?


Sorry, the confusion is my bad. I was inconsistant.

WarGame inherits from NSObject. Because NSObject has init and dealloc, any subclass of NSObject also has to have init and dealloc. So, while you can re-declare them in WarGame's interface, you don't have to.

WarDeck didn't originally had an initWithSize: method. Because this method was being introduced by WarDeck (it wasn't being inherited), I did have to included it in the interface. When I abandoned the idea of making WarDeck flexible with respect to the number of cards in a deck, the initWithSize: method changed to a plain init method. I should have removed the declaration in WarDeck's interface to make it consistant with the style of WarGame's interface.

But actually, according to my own personal preference, the correct thing to do would have been to include the missing init and dealloc declarations in WarGame's interface. This code wouldn't have passed my own code review. :p

I actually like to re-declare inherited methods in a class if those methods are being overridden, so you know as much when you're reading the interface. That is, if you see a method declared in an interface, you know there's an implementation (overridden or original) of that method in that class.

But don't forget that the interface isn't the complete catalogue of all the methods of a class. Don't forget to consider all the methods inherited from the superclass. (And there's others added by categories, but that's for another day).


I looked up this on line (NSUInteger). I guess this is the 64 bit version of an int, or unsigned int.


Get out of the habit of using straight int. It's almost always wrong these days. Get into the habit of using NSUInteger, unless you need to store negative numbers than use NSInteger. These are 64-bit integers when compiling 64-bit code, and 32-bit integers when compiling 32-bit code. NSUInteger is unsigned, while NSInteger is signed.


In warDeck.h I see ' WarDeck* theDeck;' in the section for defining variables. You then alloc and init this object in the '-(id)init' Method, I understand this part. The return type is a generic object id and if it successfully creates it's self, then that object will contain a 10 integer array, theCards of random numbers, and theIndexOfTheNextCard variable. Is that conclusion right?


Remember two step creation. (I'll keep drumming this mantra for as long as necessary). If you're considering init, then the object has already been allocated. Init is only about initialisation.

So if it successfully initialises itself, then yes, it will have initialised theCards array instance variable with 10 random cards and initialised theIndexOfTheNextCard instance variable to be zero (the index of the very first card).