Two types need to know about each other

BlitzMax Forums/BlitzMax Beginners Area/Two types need to know about each other

Czar Flavius(Posted 2007) [#1]
Let's say I have type A and type B, and the two types need to know about each other. For a simple example:

Type A
Field aB:B
End Type

Type B
Field anA:A
End Type

How can I do this, or if this can't be done, what are the alternatives?


Who was John Galt?(Posted 2007) [#2]
It can be done. You just did it. As far as I can remember, there are no forward declaration requirements as you would get in C. Try it.


CS_TBL(Posted 2007) [#3]
Is this the right thing to do however? It does make these types kinda dependent on each other. I guess it's not really a problem for one type holding a ref to another type. But what if a type must know or access more than 10 other types? 10 fields in such a type?


Czar Flavius(Posted 2007) [#4]
I have each type in a seperate Inlude file, and in the first file it complains it can't find the type from the second file.


CS_TBL(Posted 2007) [#5]
you can include a file in an include file eh..

include1.bmx:

include"include2.bmx"

codeblahblah
codeblahblah


include2.bmx:

include"include1.bmx"

codeblahblah
codeblahblah



But of course this is all prone to chaos if you ask me..

Actually I often bump onto the same situation, I'd like some more experienced programmers to comment on this issue as well.


Czar Flavius(Posted 2007) [#6]
Sorry. I made a typo in my code. My bad.

But any design suggestions or comments would still be appreciated :)


H&K(Posted 2007) [#7]
I have each type in a seperate Inlude file
Dont. Simple as that. If you need this sort of cyclic refferances, then put them in the same file simple as.

Mind you it is stupid. Type A contains a Type B, which contains a Type A, which contains a Type B, which contains a Type A, which contains a Type B, which conatains a Type A, which etc (But I assume that it was just your example to show each type referanceing the other)


Brucey(Posted 2007) [#8]
But any design suggestions

Cyclic references can imply a bad design.


Czar Flavius(Posted 2007) [#9]
Well I have a game type and a shape type for my tetris game. Perhaps because I'm not an expert programmer, the two types need to know about each other for it to work. As far as I can tell, there aren't any cyclic references.

The game type has a list of shapes in play, and the shape methods need to know about what game they are updating when they do stuff.


Sledge(Posted 2007) [#10]
The game type has a list of shapes in play, and the shape methods need to know about what game they are updating when they do stuff.


That does sound pretty bad*. Putting my procedural cap on, I could say that Tetris is an example of something that it's pretty safe to hard-code -- why have a 'game' type when you're only going to need one instance of it and that's what the compiler will spit out? Why modularise shapes via the type system when you're never going to plug them into any other sort of game? It's worth coming up with answers to these questions -- not because it's wrong to use OOP for Tetris but because it will help make OOP work better for you if its remit is clearly defined in your head.

OOP hat on: Personally I would be tempted to use the type system to make a 'game' namespace containing a (global-to-the-type) list of shapes. The shapes would be a separate type but you would access them through the 'game' namespace -- ie game.shapeList.currentShape.doStuffMethod() or game.processShapesFunction()

That way 'game' and its shapes fall naturally within each other's scope without cyclic referencing.


* EDIT: I don't want that to sound discouragingly negative -- ultimately it looks like you simply need to firmly decide which type wears the trousers and which is subordinate. Once you have that the rest should fall into place (no pun intended).

EDIT 2!
As far as I can tell, there aren't any cyclic references

If you have a game object with a list of shapes and those shapes have a field that points right back at the game instance then I think you have an unavoidably cyclic reference or two. I'd guess that manually setting the cyclic fields to NULL would let the garbage collector do its work, but designing code that confounds the GC in the first place should set alarm bells off. It's really about minimising, by design, the number of places in your code that mistakes can creep in.


Czar Flavius(Posted 2007) [#11]
If you have a game object with a list of shapes and those shapes have a field that points right back at the game instance


Nope. The game type has fields for the shapes. The shapes don't point at a game. Their update methods (place, rotate etc) take a game type as a parameter so they know which game to modify. The shapes themselves don't point back to the game.

why have a 'game' type when you're only going to need one instance of it


I was planning to have 2-player "competitive" tetris ie two games side by side. Is it still a bad idea?


H&K(Posted 2007) [#12]
I think that you are wrong to do it the way you are.

Sledge is totaly right, in that the shape Type shouldnt be dependent apon the Game type at all, otherwise there is no point in making it a full object.

I dont have your game design document, so obviously Im shooting in the dark, but the shape type shouldnt modify anything other than the shapes, it shouldnt even be aware of what its drawing the shapes to.

I would sit down and think again about how you have split your game design up into its types, and see if there are places that type "Shape" is doing things that Tyep "Game" or Type "GameDisplay" shouuld be doing.

why have a 'game' type when you're only going to need one instance of it
Its a singleton, and the most basic reason is to hold all the globals in a seperate name space

I was planning to have 2-player "competitive" tetris ie two games side by side. Is it still a bad idea?
Thats not a bad idea at all, I would be tempted to have a "Game" type for the single globals and a "Player" type for things that each player would need to have different from the other players ie Display, score etc

I agree with the ppl above who said that they dont like cyclic referenceing, but that doesnt mean that its wrong, just that probably one or both of the types were written/concieved before the full design doc was written. However I have a type "Chicken", which becomes a type "Egg", which becomes a type "Chicken" etc.

As a General rule, think what parameters an old fashioned subprogram would need, so for example the Update you mentioned.
Update needed Shape info and game info.
Well we know that Game is going to contain shapes so the main update should be in Game, which does all the stuff Shape cannot do because it doesnt know game exists then game passes update to each shape, which does its update stuff.

All this stuff depends totaly on how youve organised the whole game, but I would try when whenever possible to ensure that any type/object is as idependent of all the other objects that might contain it, as possible


Gabriel(Posted 2007) [#13]
What you might want to try is to give your game object a ShapeManager object ( a new type. ) Each game instance has a ShapeManager and anything which involves both games and shapes is done through the ShapeManager. The shapes themselves may be controlled or read by the ShapeManager, and because the ShapeManager belongs to the game, it can do other stuff with the game too. The shapes themselves cannot and won't need to.


FlameDuck(Posted 2007) [#14]
I would do what Gabe suggests, and have a "broker" object that manages communication between the two.


tonyg(Posted 2007) [#15]
The game type has a list of shapes in play, and the shape methods need to know about what game they are updating when they do stuff.


I don't fully understand why this is wrong and how it fits with the advice given or the initial question asked.
Isn''t this simply
type Tgame
   field shapes_inplay:tlist   ' pointing to tshape.list
end type
type tshape
  global list:tlist=createlist()
  field parent:tgame
end type

I do this and don't have a problem (unless I am missing something). It matches the statement I quoted but not the original posted question so I must have confused something.


Czar Flavius(Posted 2007) [#16]
Thanks for all the advice. I'll give you a simple overview of how the game works thus far.

The game type contains a list of shapes in play, as well as a 2d grid of the game board. The grid contains the information whether there is a block here, and what colour it is. It doesn't know which blocks belong to which shape.

The shape type contains a list of ie 4 points. (Point itself being a simple type with x, y.) It contains fields to know whether it is the active (player-moving) shape and the x/y position of its centre-block on the board. There are no fields of game type.

The shape's methods, such as rotate and place, takes game as a parameter so that it can access the grid. After looking at my code perhaps it should be the grid I'm passing rather than the actual game. The shape needs to know what's on the grid so that a. it can see if it can rotate/go there and b. it can update the block information on the grid if the action is ok.

As for the manager and broker objects, they sound really complicated for such a simple game. The system I have now works, so I don't want to make fundamental changes that might introduce bugs due to my lack of skill, just to make it more "elegant" a program without any other advantages.

the shape type shouldnt modify anything other than the shapes, it shouldnt even be aware of what its drawing the shapes to.


In that case I don't see how else to do it, other than move all the grid-needing rotate/place/etc methods from the shape type to the game type, so shape type is now simply a list of points and a few fields and no methods. Is that what you are suggesting?


Difference(Posted 2007) [#17]
@Czar Flavius : What you're doing sounds perfectly fine.
It's perfectly ok to have a parent reference. It's usefull for all sorts of stuff.
I'd usually put the list in the parent type like this:

type Tgame
   field shapes_inplay:tlist = New tlist 
end type

I don't get tonyg's "' pointing to tshape.list" comment.

The usefull thing is that each Tgame type can have its own list of shapes.
As far as I can tell tonyg ends up with only one list for the tshape type.


Czar Flavius(Posted 2007) [#18]
Should I move the grid to the shape type as a global? That why the shapes won't need to ask about game at all.


tonyg(Posted 2007) [#19]
As far as I can tell tonyg ends up with only one list for the tshape type.

Yes, that's right. I believe the design changed to have a single instance of game so there would be a single list of shapes. If there are more instances of game then they should each have a list.


H&K(Posted 2007) [#20]
In that case I don't see how else to do it, other than move all the grid-needing rotate/place/etc methods from the shape type to the game type, so shape type is now simply a list of points and a few fields and no methods. Is that what you are suggesting?
Well rotate for one could stay in the shape type. However I agree with Gab, and an intermeadiate "Display" type (or whatever) would deal with the stuff you couldnt get in TShape.

I think possibly there has been some confustion in this thread as to if we mean that "TShape" is a type of all the shapes possible, or a Type of all the Shapes in play at the moment. (At least from my part)


Czar Flavius(Posted 2007) [#21]
Ah. TShape..s are the shapes in play at the moment. I have a whole other TShapeTemplate ;)


Sledge(Posted 2007) [#22]
The shapes don't point at a game. Their update methods (place, rotate etc) take a game type as a parameter so they know which game to modify.

I see. To be honest, although I understand why some will tell you not to do this, I don't think it's so awful an approach. There's a temptation to chase perfect modularity in objects but if you want to actually finish anything then at some point you have to draw a line in the sand and direct all the component parts to the specific task at hand. If 'game' and 'shape' are hopelessly interdependent but ultimately destined never to exist outside of this context then knock yourself out if that's what makes finishing easier. If that's what works for you and you've no reason to be concerned about how accessible your code is for anyone else then don't be guilted into giving it up.

Do try out a plethora of design strategies, though, as you may find to your surprise that other approaches make for even more expedient development.


I was planning to have 2-player "competitive" tetris ie two games side by side. Is it still a bad idea?

No that sounds fine, 'game' being more analogous to 'session' than 'application'. It was just hard to tell from the word 'game' alone where your head was at.

Its a singleton, and the most basic reason is to hold all the globals in a seperate name space

It's worth mentioning that where you would normally have single instance of a type (with fields and methods) in Max you can instead just have a namespace (with globals and functions). I find it helps me construct more semantically pleasing code but, obviously, other folks' mileage may vary.