AI TANKS first draft

Game development specific discussions.
dafhi
Posts: 1240
Joined: Jun 04, 2005 9:51

Re: AI TANKS first draft

Postby dafhi » Sep 17, 2018 8:11

found 1 more optimization. where it writes the pixel,

int(y_des + .5)
you can ctrl-x '+ .5'

and paste it to the formula where y_des is defined
dodicat
Posts: 5879
Joined: Jan 10, 2006 20:30
Location: Scotland

Re: AI TANKS first draft

Postby dodicat » Sep 17, 2018 11:43

On tanks.
I would prefer more health and safety in the field.
32 bits only (the best compiler) -gas
https://www.freebasic.net/forum/viewtopic.php?f=2&t=21032&p=185903&hilit=%2Atank%2A#p185903
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 17, 2018 15:20

BasicCoder2 wrote:
paul doe wrote:Not always. How would you handle colliding agents?

Deal with the collision and then recompute the path.

This won't always work so nicely. See this for a demonstration of a possibly nightmarish scenario for a 'vanilla' A* implementation.
BasicCoder2 wrote:
the path is already calculated for all agents that share the same goal(s).

In the mushroom collector example they all have different homes to go to?

Let's look at a step-by-step example, using a Warcraft-like agent that has been tasked to gather wood and return it to its base so you can grasp the technique:
Image
This is the initial resource map. You can see the green squares representing a resource (let's say wood), and the black and red squares are impassable terrains that don't change at all (this is important as you'll soon see). The agent is represented as a magenta square. The green squares are the 'goal' nodes.
So, from its current position, you command the agent to 'gather wood', without explicitly telling him where. Using the resource map, it only has to follow the vector field to the closest 'goal', and gather the wood:
Image
Once it arrives at the destination (marked here in a red circle, since it's the closest point to the real goal), it gathers the resource. If the resource is to be consumed (it dissapears from the map, as the forests in Warcraft), the resource map is updated and now looks like this:
Image
So, the agent now has to return the resource to its base. To do this, it switches the vector field it uses for pathing to this one:
Image
Which is the vector field that contains the info about how to reach the 'base' from any given location. Again, green squares are goal nodes. And again, all the agent has to do is limiting himself to follow the path signaled by the vector field.

And for multiple agents:
Image
The procedure is the same. I left the initial vector that the agent uses to initiate the path. The resource map is the same for all resource-gathering agents, as you can see.

The real gist of all this is that all these vector fields can be computed once, and then used by as many agents as needed. The updates are (mostly) very local in nature, as you can see if you compare them before and after the resource has been gathered. So, updating a vector field takes little time compared with simply recalculating paths over and over.
BasicCoder2 wrote:I am not fixed on any given solution for anything. I gave the mushroom example so you could demonstrate an alternative function to ASTAR.

I would, but my implementation is far too 'complicated' to be useful to you at this point. It will be far better that you simply implement one (it's not hard; see the link I posted before, I can help you if you want) so you can have an alternative that's directly usable for you. In the process, you'll learn how to use them, and just how many amazing things you can do with them with little effort.
BasicCoder2 wrote:The bottom line is a game programmer figures out at a high level what to do and then has to have or write a set of functions to achieve that outcome. The direction, speed is how I think we think about it at a high level even if implementing it in code means converting it to a change in position along the x axis and the y axis. Think of how we might give a verbal description of how to get to some location. Agents could implement the same thing. Follow a road. Turn left at the intersection. Follow the river until you reach a bridge and cross over it. And so on ... Generating a path is really just implementing a GPS for the agent to make use of.

Yes, but not quite so. While certainly intuitive, it's not really very efficient from a performance perspective. Not to mention that, with vector fields, combined with a node graph to define the conectivity of your world representation, you can implement very cool features with little effort. Depending on how you arrange the nodes you can, for example, define portals, stairs, elevators, doors, all kinds of stuff like that, and once the vector field has been calculated, you can have your agents use all the widgets in a level in a seemingly intelligent fashion. They only follow what the vector field dictates, so you can have as many vector field calculation schemes as you can dream of.
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 18, 2018 2:33

BasicCoder2 wrote:I am not fixed on any given solution for anything. I gave the mushroom example so you could demonstrate an alternative function to ASTAR.

Speaking of which: the implementation you have is horrendously slow. A very simple optimization you can do is replace the ASTAR_GetLowestF() function (with performs a linear search over the open list) for another scheme, such as a priority queue. That way, when you add a node to the open list, you push it on the queue using its F cost as the 'priority' value. Then, when you want to retrieve the lowest F cost from the list, you only need to pop the queue and it will return it. Here's an implementation of a priority queue you can use:

Code: Select all

namespace std
  '' The null pointer
  const as any ptr NIL = cptr( any ptr, 0 )
 
  '' Defines a callback for disposal of elements of collections
  type DisposeCallback as sub( byval as any ptr )
end namespace
/'
  This is the base class for the Priority Queue class.
 
  The class represents a Queue data structure, implemented
  here as a Binary Heap. Look at these links for more info:
    https://en.wikipedia.org/wiki/Priority_queue
    https://en.wikipedia.org/wiki/Heap_(data_structure)
    https://en.wikipedia.org/wiki/Binary_heap
'/
namespace std
  namespace collections
    /'
      Represents an element of the priority queue
     
      There's no need to instantiate or extend this class, as it is
      used by the internal heap only.
    '/
    type QueueElement
      public:
        declare constructor( _
          byval as integer, _
          byval as any ptr, _
          byval as DisposeCallback = NIL )
         
        declare destructor()
         
        declare property priority() as integer
        declare property value() as any ptr
     
      private:
        declare constructor()
        /'
          These are disallowed because we really don't want any
          duplication.
        '/
        declare constructor( _
          byref as QueueElement )
        declare operator let( _
          byref as QueueElement )
         
        '' The priority of this element
        m_priority as integer
       
        '' The value associated with this element
        m_value as any ptr
       
        '' The callback function for garbage collection
        m_disposeCallback as DisposeCallback
    end type
   
    constructor QueueElement( _
      byval pPriority as integer, _
      byval pValue as any ptr, _
      byval pDisposeCallback as DisposeCallback = NIL )
       
      m_priority = pPriority
      m_value = pValue
      m_disposeCallback = pDisposeCallback
    end constructor
   
    constructor QueueElement()
    end constructor
   
    constructor QueueElement( byref rhs as QueueElement )
    end constructor
   
    operator QueueElement.let( byref rhs as QueueElement )
    end operator
   
    destructor QueueElement()
      /'
        Invoke the callback if it exists for disposal of the 'value'
        field if required.
      '/
      if( m_disposeCallback <> NIL ) then
        m_disposeCallback( m_value )
      end if
    end destructor
   
    property QueueElement.priority() as integer
      return( m_priority )
    end property
   
    property QueueElement.value() as any ptr
      return( m_value )
    end property
  end namespace
end namespace

namespace std
  namespace collections
    type Queue
      public:
        '' Public enumeration to express the priority order we want
        enum PriorityOrder
          Ascending
          Descending
        end enum
       
        declare constructor( _
          byval as uinteger )
        declare constructor( _
          byval as PriorityOrder )
        declare constructor( _
          byval as uinteger, _
          byval as PriorityOrder )
       
        declare destructor()       
       
        declare property size() as uinteger
        declare property count() as uinteger
        declare property priority() as PriorityOrder
       
        declare function push( _
          byval as integer, _
          byval as any ptr, _
          byval as DisposeCallback = NIL _
        ) as QueueElement ptr
       
        declare function pop() as any ptr
        declare function top() as any ptr
     
      private:
        declare constructor()
       
        declare sub initializeComponent()
        declare sub resize( byval as uinteger )
       
        '' The internal heap
        m_elements( any ) as QueueElement ptr
       
        '' Current size and element count
        m_size as uinteger
        m_count as uinteger
       
        '' Controls the resizing mechanism
        m_initialSize as uinteger
        m_lowerBound as uinteger
       
        '' The selected priority order for this instance
        m_priorityOrder as PriorityOrder
    end type
   
    constructor Queue( _
      byval aPriorityOrder as PriorityOrder )
      '' The default size is set to 128 items
      this.constructor( 128, aPriorityOrder )
    end constructor
   
    constructor Queue( _
      byval aSize as uinteger )     
      /'
        Note that the default priority order is set to 'Descending'
        since this property is usually understood as 'the higher
        the value, the higher the priority'
      '/
      this.constructor( aSize, PriorityOrder.Ascending )
    end constructor
 
    constructor Queue( _
      byval aSize as uinteger, _
      byval aPriorityOrder as Queue.PriorityOrder )   
      /'
        Primary constructor
       
        Note that we're using a 1-based array instead of a more common
        0-based one. This makes a lot of things easier down the road,
        especially adding an element: since we're declaring the   element
        count as unsigned, leaving a bit of space at the root of the
        heap we don't have to check for underflow conditions during the
        sifting.
      '/
      m_size = iif( aSize < 1, 1, aSize )
      m_priorityOrder = aPriorityOrder
   
      redim m_elements( 1 to m_size )
     
      m_count = 0
      m_initialSize = m_size
      m_lowerBound = 1
    end constructor
   
    destructor Queue()
      '' Delete all elements if the heap
      for i as integer = 1 to m_count
        delete( m_elements( i ) )
      next
    end destructor
 
    property Queue.count() as uinteger
      return( m_count )
    end property
 
    property Queue.size() as uinteger
      return( m_size )
    end property
   
    property Queue.priority() as PriorityOrder
      return( m_priorityOrder )
    end property
   
    sub Queue.resize( byval newSize as uinteger )
      /'
        Resizes the internal array used as the heap.
       
        The algorithm works like this:
        When you instantiate the list, the initial size is recorded, and
        is used to determine when the list is resized, and by how much. In
        this case, the array grows every time the number of items exceed
        the initial value by half, and shrinks every time that the previous
        size is exceeded also by half.
     
        This way, you'll always have a 'window', centered around the current
        item count. Hence, most of the resizing will happen during bulk
        addition or deletion operations, not when there's a relatively
        balanced number of them.
      '/
      newSize = iif( newSize < m_initialSize, _
        m_initialSize, newSize )
   
      m_size = newSize
   
      m_lowerBound = m_size - m_initialSize - ( m_initialSize shr 1 )
      m_lowerBound = iif( m_lowerBound < m_initialSize, _
        1, m_lowerBound )
   
      redim preserve m_elements( 1 to m_size )      
    end sub
   
    function Queue.push( _
      byval pPriority as integer, _
      byval pValue as any ptr, _
      byval pDisposeCallback as sub( byval as any ptr ) = NIL _
    ) as QueueElement ptr
      /'
        Enqueues (adds) an element to the tail of the heap
       
        Whenever one adds an element to a binary heap, the heap has to be
        rebalanced (a process known as 'sifting') to leave it in a valid
        state. The procedure starts at the tail and proceeds towards the root.
       
        Look here for a reference on how this is implemented:
        https://en.wikipedia.org/wiki/Heap_(data_structure)#Implementation
      '/
      '' This will hold the added element
      dim as QueueElement ptr newElement
     
      '' Increment the number of elements in the heap
      m_count += 1
     
      '' Resize the internal heap if needed
      if( m_count > m_size - m_initialSize shr 1 ) then
        resize( m_size + m_initialSize )
      end if
     
      '' Set the current element position to the tail of the heap
      dim as uinteger elementPosition = m_count
     
      '' Creates the QueueElement associated with this position
      newElement = new QueueElement( pPriority, pValue )
      m_elements( elementPosition ) = newElement
     
      '' The parent position of this element
      dim as uinteger parentPosition
     
      '' Controls if the element needs to be swapped with its parent
      dim as boolean needSwap
     
      /'
        Sift the heap until the enqueued element reaches its correct
        position or it bubbles all the way to the root of the heap.
       
        The parent position of the considered element can be computed
        as the considered element position \ 2 in a 1-based array.
      '/
      do
        parentPosition = elementPosition shr 1
       
        if( parentPosition > 0 ) then
          '' Reset the boolean flag
          needSwap = false
         
          /'
            Depending on the priority order, see if we need to swap the
            considered element with its parent.
          '/
         
          select case as const m_priorityOrder
            case PriorityOrder.Ascending
              if( m_elements( elementPosition )->priority < _
                m_elements( parentPosition )->priority ) then
               
                /'
                  Swap positions if the considered element's priority is
                  *lower* than that of its parent
                '/
                needSwap = true
              end if
           
            case PriorityOrder.Descending
              if( m_elements( elementPosition )->priority > _
                m_elements( parentPosition )->priority ) then
                /'
                  Swap positions if the considered element's priority is
                  *higher* than that of its parent
                '/
                needSwap = true
              end if
          end select
            /'
              Swap the considered element with its parent and update the
              element's position to that of its parent to continue the
              sifting.
            '/
          if( needSwap = true ) then
            swap m_elements( elementPosition ), m_elements( parentPosition )
            elementPosition = parentPosition
          else
            '' No need to swap, element is already at the correct position
            exit do
          end if
        else
          '' Element is already at the root, end the sifting
          exit do
        end if
      loop
     
      /'
        Returns the newly enqueued element
       
        This allows to cache the element for fast lookups if needed. Be
        careful how you use this!
      '/
      return( newElement )
    end function
   
    function Queue.pop() as any ptr
      /'
        Dequeues (removes) an element from the heap
       
        The heap has to be sifted also when removing elements, this time
        starting from the head (the root) element and proceeding towards
        the tail.
      '/
     
      if( m_count > 0 ) then
        '' Fetch the 'value' at the root of the heap
        dim as any ptr elementValue = m_elements( 1 )->value
       
        '' Delete the QueueElement associated with it
        delete( m_elements( 1 ) )
        m_elements( 1 ) = NIL
       
        '' Then, bring the element on the tail of the heap to the root
        swap m_elements( 1 ), m_elements( m_count )
       
        '' Decrement the element count to account for the removed element
        m_count -= 1
       
        '' Resize the internal heap if needed
        if( m_count < m_lowerBound ) then
          resize( m_size - m_initialSize )
        end if
       
        /'
          Here, the element at the root of the heap is successively checked
          against its two siblings to swap their positions if necessary. The
          current element position is set at the root of the heap to start
          the sifting.
        '/
        dim as uinteger elementPosition = 1
       
        '' The computed positions of both siblings of the considered element
        dim as uinteger child1Position
        dim as uinteger child2Position
       
        '' The potentially new position the element could take
        dim as uinteger newPosition
       
        '' Flag to determine if we swap the element or not
        dim as boolean needSwap
        /'
          If there's still elements remaining in the heap, we need to reorder them
          to account for the removal of it's previous root element. The sifting
          direction depends on how the class was instantiated (in 'Ascending' or
          'Descending' priority order).
        '/
       
        do while( m_count > 0 )
          '' Compute the positions of the two siblings of the element
          child1Position = elementPosition shl 1
          child2Position = child1Position + 1
         
          '' Check if the element has one or two siblings
          if( child1Position <= m_count ) then
            if( child2Position <= m_count ) then
              /'
                The element has two siblings, see which of the two we need to swap
                according to the desired priority order, and record its position.
              '/
              select case as const m_priorityOrder
                case PriorityOrder.Ascending
                  '' Set the new potential position to the *lowest* of the two siblings
                  newPosition = iif( _
                    m_elements( child1Position )->priority < m_elements( child2Position )->priority, _
                    child1Position, child2Position )
             
                case PriorityOrder.Descending
                  '' Set the new potential position to the *highest* of the two siblings
                  newPosition = iif( _
                    m_elements( child1Position )->priority > m_elements( child2Position )->priority, _
                    child1Position, child2Position )
              end select
            else
              '' Only one sibling left, always record its position
              newPosition = child1Position
            end if
           
            '' Reset the flag
            needSwap = false
           
            '' Check to see if we need to swap the element with its sibling
            select case as const m_priorityOrder
              case PriorityOrder.Ascending
                needSwap = iif( _
                  m_elements( elementPosition )->priority > m_elements( newPosition )->priority, _
                  true, false )
             
              case PriorityOrder.Descending
                needSwap = iif( _
                  m_elements( elementPosition )->priority < m_elements( newPosition )->priority, _
                  true, false )
            end select
            /'
              If needed, swap the considered element's position with the new position computed
              above, and set the current element position to the new one to continue the sifting
              from there.
            '/
            if( needSwap = true ) then
              swap m_elements( elementPosition ), m_elements( newPosition )
              elementPosition = newPosition
            else
              '' Element is already in its correct position
              exit do
            end if
          else
            '' Element bubbled to the tail of the heap
            exit do
          end if
        loop
       
        '' Return the root element value
        return( elementValue )
      end if
     
      '' If there's no elements in the heap, return a null pointer
      return( NIL )
    end function
   
    function Queue.top() as any ptr
      /'
        Peeks the top value field of the root of the heap, without removing it.
       
        If you specified a dispose callback for elements in the heap, be
        careful not to delete them manually using this method or you'll crash
        the app when the heap is deleted (since it'll invoke a function pointer
        on an element that's not there anymore).
       
        It will return a null pointer if the heap is empty.
      '/
      if( m_count > 0 ) then
        return( m_elements( 1 )->value )
      else
        return( NIL )
      end if
    end function
  end namespace
end namespace

/'
  Some test code
'/
'' A simple type to order by age
type Record
  public:
    declare constructor( _
      byref as const string, _
      byref as integer )
    declare destructor()
   
    declare property name() as string
    declare property age() as integer
 
  private:
    declare constructor()
   
    m_name as string
    m_age as integer
end type

constructor Record()
end constructor

constructor Record( _
  byref theName as const string, _
  byref theAge as integer )
 
  m_name = theName
  m_age = theAge
end constructor

destructor Record()
end destructor

property Record.name() as string
  return( m_name )
end property

property Record.age() as integer
  return( m_age )
end property

using std.collections

'' Define some records
var record1 = Record( "John Doe", 34 )
var record2 = Record( "Jane Doe", 32 )
var record3 = Record( "Minnie Doe", 12 )

'' Instantiate a queue
var aQueue = Queue( Queue.PriorityOrder.Ascending )

'' Push the records on the queue using age as the priority
aQueue.push( record1.age, @record1 )
aQueue.push( record2.age, @record2 )
aQueue.push( record3.age, @record3 )

'' And show them
dim as Record ptr aRecord

do while( aQueue.count > 0 )
  aRecord = aQueue.pop()
 
  ? aRecord->name, aRecord->age
loop

?

'' Instantiate another queue with descending priority
aQueue = Queue( Queue.PriorityOrder.Descending )

'' Push the records on the queue using age as the priority
aQueue.push( record1.age, @record1 )
aQueue.push( record2.age, @record2 )
aQueue.push( record3.age, @record3 )

'' And show them
do while( aQueue.count > 0 )
  aRecord = aQueue.pop()
 
  ? aRecord->name, aRecord->age
loop

sleep()

It comes with a little test code, so you can see how this works.
dafhi
Posts: 1240
Joined: Jun 04, 2005 9:51

Re: AI TANKS first draft

Postby dafhi » Sep 18, 2018 2:58

The Heap is on my list of things to grok. One of my favorite quotes is "I am an eternal being. There is no rush."

And yet, I have so many questions
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 18, 2018 3:09

dafhi wrote:And yet, I have so many questions

That won't get answers if you don't ask them =D
dafhi
Posts: 1240
Joined: Jun 04, 2005 9:51

Re: AI TANKS first draft

Postby dafhi » Sep 18, 2018 3:25

Heh. I could end up hi-jacking every thread in the galaxy. Alright I'll just ask one. So with my minibits class, i can't help notice from the perspective of SOLID, i'm repeating a habit. So if I was to rewrite, I'd imagine just keep the constructors, Lets, and at least one Cast, which are all pretty much the intent of the class, .. well .. maybe not. Things depend upon .cBits property, and at the very least, 1 pre-calc: The 'u' which is 2^_cBits - 1. I'm gonna rename 'u' to mask.

Anyway, any thoughts?
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 18, 2018 3:36

dafhi wrote:Heh. I could end up hi-jacking every thread in the galaxy.

You could just open a new thread, you know.
dafhi wrote:Alright I'll just ask one. So with my minibits class, i can't help notice from the perspective of SOLID, i'm repeating a habit. So if I was to rewrite, I'd imagine just keep the constructors, Lets, and at least one Cast, which are all pretty much the intent of the class, .. well .. maybe not. Things depend upon .cBits property, and at the very least, 1 pre-calc: The 'u' which is 2^_cBits - 1. I'm gonna rename 'u' to mask.

Anyway, any thoughts?

Ah, SOLID. I promised we'll talk about it, and I stumbled upon something that smelt like a game and totally forgot =D
But, what is the concrete question, then? Which is the habit that you're repeating? Wouldn't it be better if we discuss these things in the appropriate thread?
BasicCoder2
Posts: 3385
Joined: Jan 01, 2009 7:03

Re: AI TANKS first draft

Postby BasicCoder2 » Sep 18, 2018 5:10

The field vector topic needs its own thread.
We need to be conscious of the fact we are looking at this "game writing" from different levels. To repeat what I wrote earlier:
A game programmer figures out at a high level what to do and then has to have, or write, a set of functions to achieve that outcome.
So writing a game may be the goal and details about how a used function works or can be optimised is a side issue best left to those who have the time and skill level required.
Last edited by BasicCoder2 on Sep 18, 2018 8:16, edited 2 times in total.
dafhi
Posts: 1240
Joined: Jun 04, 2005 9:51

Re: AI TANKS first draft

Postby dafhi » Sep 18, 2018 5:12

sorry :P
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 18, 2018 9:01

BasicCoder2 wrote:The field vector topic needs its own thread.
We need to be conscious of the fact we are looking at this "game writing" from different levels. To repeat what I wrote earlier:
A game programmer figures out at a high level what to do and then has to have, or write, a set of functions to achieve that outcome.
So writing a game may be the goal and details about how a used function works or can be optimised is a side issue best left to those who have the time and skill level required.

I see. Goodbye, then.
BasicCoder2
Posts: 3385
Joined: Jan 01, 2009 7:03

Re: AI TANKS first draft

Postby BasicCoder2 » Sep 18, 2018 18:48

paul doe wrote:
BasicCoder2 wrote:We need to be conscious of the fact we are looking at this "game writing" from different levels. To repeat what I wrote earlier:
A game programmer figures out at a high level what to do and then has to have, or write, a set of functions to achieve that outcome.
So writing a game may be the goal and details about how a used function works or can be optimised is a side issue best left to those who have the time and skill level required.

I see. Goodbye, then.

What do you see? Are you are only interested in teaching someone how to write say a path finding routine but you are not interested in actually providing one?

If we had to do everything ourselves from scratch we wouldn't achieve very much at all.

Working together involves a division of labour according to each person's interests and skill levels. The idea of a FreeBASIC Community produced game would have required such cooperation.
paul doe
Posts: 916
Joined: Jul 25, 2017 17:22
Location: Argentina

Re: AI TANKS first draft

Postby paul doe » Sep 18, 2018 19:17

BasicCoder2 wrote:What do you see? Are you are only interested in teaching someone how to write say a path finding routine but you are not interested in actually providing one?

I see that you're not interested in learning. I said that you'll be better off implementing one (and I also offered my help in doing so) than using an implementation provided by me, since you won't be able to use it (at least, not yet). So no, I'm not interested. I already did so (in the 3D without OpenGL, remember?), and it was a major waste of my time. Did you learnt something from that? No. Are you able to effectively use it? No, and it's a piece of crap (by my standards, at least).

Did you read the link that I provided? I don't think so. If you did, you'll realize that it'll take you, I don't know, 20 minutes? to implement one yourself. So, you're asking me to spoon feed you because you don't want to spend 20 minutes, and you want me to spend time nerfing down my implementation (which is non-trivial) so you can understand and use it? Thanks, I think I'll pass on that one.
BasicCoder2 wrote:Working together involves a division of labour according to each person's interests and skill levels. The idea of a FreeBASIC Community produced game would have required such cooperation.

I didn't know that we were working together, and this is not the Community Produced Game thread. I was only suggesting something, and I think that I'm quite entitled to do so as long as it adds something to the discussion. If you don't think so, please, tell me so I won't waste any more time talking to you.
badidea
Posts: 1414
Joined: May 24, 2007 22:10
Location: The Netherlands

Re: AI TANKS first draft

Postby badidea » Sep 18, 2018 19:27

Here is some code that could be converted with some** effort: Flow Field Pathfinding
"&lt;" is "<" and "&gt;" is ">"
**List functionality is needed as well
BasicCoder2
Posts: 3385
Joined: Jan 01, 2009 7:03

Re: AI TANKS first draft

Postby BasicCoder2 » Sep 18, 2018 20:20

paul doe wrote:I see that you're not interested in learning.


That is untrue. I devour books and tv documentaries on many science subjects. I taught myself basic electronics and the little I was able to learn about programming. However I am not an academic who can spend hours on these subjects. I worked physically for a living and my spare time wasn't all spent learning stuff. I do have other interests.

I do have my limitations which I have never tried to conceal. Just because someone likes something doesn't mean they are naturally good at it. For example I would have loved to have a clear grasp on calculus which came up recently with my desire to understand the back propagation algorithm in programming a working ANN. I have never given up on trying to understand calculus. Just two days ago I borrowed a book from the library called "The Calculus Diaries" by Jennifer Coellette.

Before I started using FreeBASIC I was trying to teach myself C++ and how to use DirectX. The reason I used FreeBASIC instead is because it provided the graphic commands I needed for my project at the time so I didn't need to learn how to use DirectX. Indeed the reason I use a high level language at all is because I don't need to learn how to write all the stuff in Assembler (my first language). A HLL provides routines to do things such as evaluate an expression or access the Windows API bitmap routines although as a mathematician and advanced programmer that would be child's play for you.

Did you read the link that I provided? I don't think so. If you did, you'll realize that it'll take you, I don't know, 20 minutes? to implement one yourself.


If you mean the field vector stuff then yes I did read it but you over estimate my abilities to easily understand or implement many things.

So, you're asking me to spoon feed you because you don't want to spend 20 minutes, and you want me to spend time nerfing down my implementation (which is non-trivial) so you can understand and use it?


No you offered, I didn't ask and you really do overestimate my intellectual abilities. I would have liked to have understood your nerfed down explanations but I didn't. I was very interested in the thought of being able to write a simple 3d world made out of polygons but it turned out to be too hard.

I didn't know that we were working together, and this is not the Community Produced Game thread.


Even a student/teacher relationship is working together. And exchanges in the forum I take to be "working together" unless of course it is just posting some code to show off your programming skills.

I was only suggesting something, and I think that I'm quite entitled to do so as long as it adds something to the discussion.


Of course. And if you mean field vectors I have been reading more about them and even started trying to write some code. It is not complete enough yet to post but here is the display so far. However should I get a complete demo working it would deserve its own thread.
Image

Return to “Game Dev”

Who is online

Users browsing this forum: No registered users and 5 guests