FreeBASIC Community produced game

General discussion for topics related to the FreeBASIC project or its community.
Post Reply
BasicCoder2
Posts: 3906
Joined: Jan 01, 2009 7:03
Location: Australia

Re: FreeBASIC Community produced game

Post by BasicCoder2 »

paul doe wrote:
BasicCoder2 wrote:So I assume here anyone can add their own AI driven spaceship to test in combat? That might be an interesting programming problem.
Yep, that was the original idea. As coderJeff stated early, the idea was good but the implementation was brittle, so he wants to (essentially) write a more robust and flexible one.
A FreeBASIC version of this?
http://robocode.sourceforge.net/
https://www.youtube.com/watch?v=ZcMLRwu0i-k
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: FreeBASIC Community produced game

Post by paul doe »

fxm wrote:Therefore, in your code, 2 abstract procedure definitions are missing: done() and update() in Subject.
These are not part of the subject's responsibilities and thus, need to be segregated. As I said before, these are old snippets and illustrate only the pattern, not the technicalities. The completely 'correct' version for me would be:

Code: Select all

/'
  Observer pattern
'/

'' We need this to forward reference the Observer interface
type _Observer as Observer
'' Null for objects
#define NIL cptr( any ptr, 0 )

/'
  Context interface
 
  This interface defines the context for the notification, that is,
  the parameters for the message sent to the listener. It empty
  because it will vary with each individual context (which, in turn,
  depend on the interests of each observer).
  The default constructor is declared protected: to avoid direct
  instantiation of this class.
'/
type Context extends Object
  public:
    declare virtual destructor()
 
  protected:
    declare constructor()
end type

constructor Context()
end constructor

destructor Context()
end destructor

/'
  Subject interface
 
  This interface defines the 'subject' of the pattern, that is, the
  object that acts as the message dispatch service. Observers subscribe
  to receive notifications from him.
'/
type Subject extends Object
  public:
    declare virtual destructor()
   
    declare abstract sub subscribe( _
      byref as _Observer )
    declare abstract sub unsubscribe()
    declare abstract sub sendMessage( _
      byref as Context )
 
  protected:
    declare constructor()
end type

constructor Subject()
end constructor

destructor Subject()
end destructor

type PollingSubject extends Subject
  declare virtual destructor()
  declare abstract property done() as boolean
  declare abstract sub update()
end type

destructor PollingSubject()
end destructor

/'
  Observer interface
 
  This is the interface that defines the 'observers' of the pattern. They
  subscribe to receive notifications from a 'subject' when needed.
'/
type Observer extends Object
  public:
    declare virtual destructor()
   
    declare abstract sub receiveMessage( _
      byref as Context )
   
  protected:
    declare constructor()
end type

constructor Observer()
end constructor

destructor Observer()
end destructor

/'
  A concrete implementation of a context. In this case, all it does is
  store a key pressed.
'/
type KeyboardContext extends Context
  public:
    declare constructor( _
      byval as ulong )
    declare destructor() override
   
    whichKey as ulong
  
  private:
    declare constructor()
end type

constructor KeyboardContext()
end constructor

constructor KeyboardContext( _
  byval aKey as ulong )
 
  whichKey = aKey
end constructor

destructor KeyboardContext()
end destructor

/'
  A concrete implementation of a subject. In this case, this is a
  simple keyboard handler that will send a message informing the
  subscribed observers of which key has been pressed. Here, there's
  support for a single observer, but this can be easily changed to
  support more than one observer per subject (by maintaining a list
  of observers).
'/
type KeyboardSubject extends PollingSubject
  public:
    declare constructor( _
      byref as Observer )
    declare destructor() override
    
    declare sub update() override
    declare property done() as boolean override
    
    declare sub subscribe( _
      byref as Observer ) override
    declare sub unsubscribe() override
    declare sub sendMessage( _
      byref as Context ) override
  
  private:
    declare constructor()
    
    m_observer as Observer ptr
    m_done as boolean
end type

constructor KeyboardSubject()
end constructor

constructor KeyboardSubject( _
  byref anObserver as Observer )
  
  m_observer = @anObserver
end constructor

destructor KeyboardSubject()
end destructor

property KeyboardSubject.done() as boolean
  return( m_done )
end property

sub KeyboardSubject.subscribe( _
  byref anObserver as Observer )
 
  m_observer = @anObserver
end sub

sub KeyboardSubject.unsubscribe()
  m_observer = NIL
end sub

sub KeyboardSubject.sendMessage( _
  byref aContext as Context )
 
  if( m_observer <> NIL ) then
    m_observer->receiveMessage( aContext )
  end if
end sub

sub KeyboardSubject.update()
  dim as string k = inkey()
 
  if( len( k ) > 0 ) then
    sendMessage( KeyboardContext( asc( k ) ) )
  end if
  
  if( k = chr( 27 ) ) then
    m_done = true
  end if
end sub

/'
  And this is a concrete implementation of an observer. Here, it
  simply listens to messages forwarded by its subject, and prints
  some text when it receives one.
'/
type KeyboardObserver extends Observer
  public:
    declare constructor()
    declare destructor() override
   
    declare sub receiveMessage( _
      byref as Context ) override
end type

constructor KeyboardObserver()
end constructor

destructor KeyboardObserver()
end destructor

sub KeyboardObserver.receiveMessage( _
  byref aContext as Context )
 
  ? "A key has been pressed!"
  var aKey = cptr( _
    KeyboardContext ptr, @aContext )->whichKey
 
  ? "ASCII code: "; aKey
end sub

/'
  Main code
'/
var theObserver = KeyboardObserver()
var theSubject = KeyboardSubject( theObserver )

dim as PollingSubject ptr aSubject = @theSubject

do
  '' Allow the subject to do any processing it needs
  aSubject->update()
 
  /'
    Loops until the subject signals it's done processing. This is
    NOT a good design, just made it here like this for convenience.
  '/
  sleep( 1, 1 )
loop until( aSubject->done )
However, this also introduces a little more complexity (which wasn't the intention in the first snippet). One little snippet at a time ;)
Yes, but with starships. Nice find! =D
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC Community produced game

Post by fxm »

paul doe wrote:
fxm wrote:Therefore, in your code, 2 abstract procedure definitions are missing: done() and update() in Subject.
These are not part of the subject's responsibilities and thus, need to be segregated. As I said before, these are old snippets and illustrate only the pattern, not the technicalities. The completely 'correct' version for me would be:
.....
.....
However, this also introduces a little more complexity (which wasn't the intention in the first snippet). One little snippet at a time ;)
Ok, but only a small addition to better understand now the interest of such a principle :-)
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: FreeBASIC Community produced game

Post by paul doe »

fxm wrote:Ok, but only a small addition to better understand now the interest of such a principle :-)
My, of course. Just stating the reasons for the crappy design =D
badidea
Posts: 2586
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: FreeBASIC Community produced game

Post by badidea »

paul doe wrote:However, this also introduces a little more complexity (which wasn't the intention in the first snippet). One little snippet at a time ;)
I agree, still studying the code :-) For me it seems like a very complicated way to do this:

Code: Select all

dim as ubyte key
do
	do
		sleep 1,1
		key = asc(inkey())
	loop while key = 0
	? "A key has been pressed!"
	? "ASCII code: "; key
loop while key <> 27
But I assume that the real power becomes clear when multiple subjects and observers are introduced with a message cue containing messages in various formats?
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: FreeBASIC Community produced game

Post by paul doe »

badidea wrote:I agree, still studying the code :-) For me it seems like a very complicated way to do this:
Yes, it is =D

What I mean is, it doesn't matter much what it does, but how it does it, and the implications of doing it this way.
badidea wrote:But I assume that the real power becomes clear when multiple subjects and observers are introduced with a message cue containing messages in various formats?
Exactly. That's precisely the point of the entire OOP thing. And if I de-abstract the code a little, is this easier to understand?

Code: Select all

/'
  Observer pattern
'/

'' We need this to forward reference the Observer interface
type _Observer as Observer
'' Null for objects
#define NIL cptr( any ptr, 0 )

/'
  Context interface
 
  This interface defines the context for the notification, that is,
  the parameters for the message sent to the listener. It empty
  because it will vary with each individual context (which, in turn,
  depend on the interests of each observer).
  The default constructor is declared protected: to avoid direct
  instantiation of this class.
'/
type Context extends Object
  public:
    declare virtual destructor()
 
  protected:
    declare constructor()
end type

constructor Context()
end constructor

destructor Context()
end destructor

/'
  Subject interface
 
  This interface defines the 'subject' of the pattern, that is, the
  object that acts as the message dispatch service. Observers subscribe
  to receive notifications from him.
'/
type Subject extends Object
  public:
    declare virtual destructor()
   
    declare abstract sub subscribe( _
      byref as _Observer )
    declare abstract sub unsubscribe()
    declare abstract sub sendMessage( _
      byref as Context )
 
  protected:
    declare constructor()
end type

constructor Subject()
end constructor

destructor Subject()
end destructor

type PollingSubject extends Subject
  declare virtual destructor()
  declare abstract property done() as boolean
  declare abstract sub update()
end type

destructor PollingSubject()
end destructor

/'
  Observer interface
 
  This is the interface that defines the 'observers' of the pattern. They
  subscribe to receive notifications from a 'subject' when needed.
'/
type Observer extends Object
  public:
    declare virtual destructor()
   
    declare abstract sub receiveMessage( _
      byref as Context )
   
  protected:
    declare constructor()
end type

constructor Observer()
end constructor

destructor Observer()
end destructor

/'
  A concrete implementation of a context. In this case, all it does is
  store a key pressed.
'/
type Keypressed extends Context
  public:
    declare constructor( _
      byval as ulong )
    declare destructor() override
   
    whichKey as ulong
  
  private:
    declare constructor()
end type

constructor Keypressed()
end constructor

constructor Keypressed( _
  byval aKey as ulong )
 
  whichKey = aKey
end constructor

destructor Keypressed()
end destructor

/'
  A concrete implementation of a subject. In this case, this is a
  simple keyboard handler that will send a message informing the
  subscribed observers of which key has been pressed. Here, there's
  support for a single observer, but this can be easily changed to
  support more than one observer per subject (by maintaining a list
  of observers).
'/
type MainEventHandler extends PollingSubject
  public:
    declare constructor( _
      byref as Observer )
    declare destructor() override
    
    declare sub update() override
    declare property done() as boolean override
    
    declare sub subscribe( _
      byref as Observer ) override
    declare sub unsubscribe() override
    declare sub sendMessage( _
      byref as Context ) override
    
  private:
    declare constructor()
    
    m_observers( any ) as Observer ptr
    m_observerCount as integer
    
    m_done as boolean
end type

constructor MainEventHandler()
end constructor

constructor MainEventHandler( _
  byref anObserver as Observer )
  
  subscribe( anObserver )
end constructor

destructor MainEventHandler()
end destructor

property MainEventHandler.done() as boolean
  return( m_done )
end property

sub MainEventHandler.subscribe( _
  byref anObserver as Observer )
 
  m_observerCount += 1
  redim preserve m_observers( 0 to m_observerCount - 1 )
  
  m_observers( m_observerCount - 1 ) = @anObserver
end sub

sub MainEventHandler.unsubscribe()
  '' Code to unsubscribe an observer from the list
end sub

sub MainEventHandler.sendMessage( _
  byref aContext as Context )
  
  for i as integer = 0 to m_observerCount - 1
    m_observers( i )->receiveMessage( aContext )
  next
end sub

sub MainEventHandler.update()
  dim as string k = inkey()
 
  if( len( k ) > 0 ) then
    sendMessage( Keypressed( asc( k ) ) )
  end if
  
  if( k = chr( 27 ) ) then
    m_done = true
  end if
end sub

/'
  And this is a concrete implementation of an observer. Here, it
  simply listens to messages forwarded by its subject, and prints
  some text when it receives one.
'/
type Player extends Observer
  public:
    declare constructor()
    declare destructor() override
   
    declare sub receiveMessage( _
      byref as Context ) override
end type

constructor Player()
end constructor

destructor Player()
end destructor

sub Player.receiveMessage( _
  byref aContext as Context )
 
  ? "Player has pressed a key!"
  var aKey = cptr( _
    Keypressed ptr, @aContext )->whichKey
end sub

type Enemy extends Observer
  public:
    declare constructor()
    declare destructor()
    
    declare sub receiveMessage( _
      byref as Context ) override
end type

constructor Enemy()
end constructor

destructor Enemy()
end destructor

sub Enemy.receiveMessage( _
  byref aContext as Context )
  
  ? "<Enemy AI reacts to player input>"
end sub
 
/'
  Main code
'/
var thePlayer = Player()
var globalEventHandler = MainEventHandler( thePlayer )

dim as integer enemies = 5
dim as Enemy theEnemies( 0 to enemies - 1 )

for i as integer = 0 to enemies - 1
  globalEventHandler.subscribe( theEnemies( i ) )
next

do
  '' Allow the subject to do any processing it needs
  globalEventHandler.update()
 
  /'
    Loops until the subject signals it's done processing. This is
    NOT a good design, just made it here like this for convenience.
  '/
  sleep( 1, 1 )
loop until( globalEventHandler.done )
Obviously, the main event handler for a game is far more complicated than this, but I hope that it illustrates the point. As you can see, the main loop hasn't changed at all, and it doesn't even need to. It can accomodate any kind of player, enemy, or message; all those things are abstracted away. You don't need to change anything to add new enemies, a remote player, or different kinds of messages (or 'events' if you will). This implementation is very limited, but I can post a real implementation if you wish. I'd suggest that you try to understand this simple implementation first =D
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: FreeBASIC Community produced game

Post by MrSwiss »

The OOP view of things: everything abstracted from the *real world* look/feel.
(by adding layer uppon layer of obscurity, until nobody sees the *flesh on the bone* any longer)

This doesn't mean that I don't understand the code, what I'm currently not seeing is:
*the advantage* it's supposed to *deliver*. (in a way, that procedural can't)
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: FreeBASIC Community produced game

Post by grindstone »

badidea wrote:Your code is like a Chinese crossword puzzle to me.
Thank you, I'm glad that I'm not the only one. <grin>

Meanwhile I understand the intention, but the implementation is still all Greek to me. From my actual point of view this is unnecessary complicated.
fxm
Moderator
Posts: 12081
Joined: Apr 22, 2009 12:46
Location: Paris suburbs, FRANCE

Re: FreeBASIC Community produced game

Post by fxm »

Now I regret to have sparked such a debate here in this topic that was not intended for it.
I think that if people want to approach or discuss around such a subject (OOP / abstraction), they only have to initiate new topics in the "general" forum (starting to discuss simpler structures).
Last edited by fxm on Aug 31, 2018 16:04, edited 1 time in total.
grindstone
Posts: 862
Joined: May 05, 2015 5:35
Location: Germany

Re: FreeBASIC Community produced game

Post by grindstone »

fxm wrote:Now I regret to have sparked such a debate here in this topic
Why that? The way how the communication is implemented is undoubtedly relevant for such a game.
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: FreeBASIC Community produced game

Post by MrSwiss »

fxm wrote:Now I regret to have sparked such a debate here in this topic that was not intended for it.
You didn't, it was bound to happen with paul doe onboard, since he's all OOP (and, not procedural).

Btw: I tend to agree with Boris the Old's: Five simple Rules on using OOP.
dafhi
Posts: 1640
Joined: Jun 04, 2005 9:51

Re: FreeBASIC Community produced game

Post by dafhi »

paul doe wrote:
badidea wrote:
badidea wrote:But I assume that the real power becomes clear when multiple subjects and observers are introduced with a message cue containing messages in various formats?
Exactly. That's precisely the point of the entire OOP thing. And if I de-abstract the code a little, is this easier to understand?
MrSwiss
Posts: 3910
Joined: Jun 02, 2013 9:27
Location: Switzerland

Re: FreeBASIC Community produced game

Post by MrSwiss »

@dafhi, you clearly didn't understand: I don't buy paul doe's explanations!

My approach is different: keep it as simple as possible (OOP only, if unavoidable!).
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: FreeBASIC Community produced game

Post by paul doe »

MrSwiss wrote:This doesn't mean that I don't understand the code, what I'm currently not seeing is:
*the advantage* it's supposed to *deliver*. (in a way, that procedural can't)
grindstone wrote:Meanwhile I understand the intention, but the implementation is still all Greek to me. From my actual point of view this is unnecessary complicated.
That's ok. I won't waste my time arguing on this one. You can always ask google about this, and seek the answers that better line up with your way of thinking, be it that OOP is a piece of crap or is the be-all end-all of programming paradigms. Or you can just see it as a way of expressing a solution that offers advantages and disadvantages, like everything else in life =D
fxm wrote:Now I regret to have sparked such a debate here in this topic that was not intended for it.
Why? It was only a matter of time. In fact, I was pretty surprised it took so long ;)
grindstone wrote:Why that? The way how the communication is implemented is undoubtedly relevant for such a game.
Indeed, but I'd say that it's a little early to worry about those things yet.
MrSwiss wrote:You didn't, it was bound to happen with paul doe onboard, since he's all OOP (and, not procedural).

Btw: I tend to agree with Boris the Old's: Five simple Rules on using OOP.
Interesting, but I prefer S.O.L.I.D. and G.R.A.S.P..

@all other people, reading this thread:
I don't want yet another stupid argument that leads to nowhere and having to justify every little snippet of code I post is just ludicrous. If you think that OOP sucks a$$, then fine, don't use it and throw all the crap you want at it. If you think that OOP is a blessing from the heavens, you'll soon awaken from your dream.

But if I'm actually the problem here, then just tell me and I'll immediately return to minding my own business and stop contributing anything to this thread at once.

Let's focus on the design aspect of the game for now, shall we? Things such as ideas, concept art, gameplay aspects, that sort of thing. Implementation details will come at a later stage. Agree?
Image
paul doe
Moderator
Posts: 1730
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: FreeBASIC Community produced game

Post by paul doe »

MrSwiss wrote:@dafhi, you clearly didn't understand: I don't buy paul doe's explanations!
You don't have to buy anything, that's just what the code does =D
MrSwiss wrote:My approach is different: keep it as simple as possible (OOP only, if unavoidable!).
I also don't use OOP for everything. Some things are better expressed just using procedural programming. FreeBasic (unlike eg. Java) allow mixed-paradigm programming. Why limit oneself to just one?
Post Reply