Page 1 of 1

Semi-intelligent, multi-agent, auction hosting.. Rate Topic: -----

#1 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 8396
  • View blog
  • Posts: 31,234
  • Joined: 12-June 08

Posted 18 August 2012 - 11:46 AM

*
POPULAR

Topics covered:
  • Basic Object creation
  • Overriding functions
  • Serialization of objects
  • TCPClient transmission
  • Binary formatter


Tools required:
Visual Studio 2010
.NET 4.0

Preface 1: For this example I am using three virtual machines in VMware player.. you can use multiple networked pcs or even virtualbox to test this out. Installing and running a virtual machine (VM) is a bit outside of the scope of this tutorial. Just remember when you do this the quick and dirty way is to have the VMs all on a bridged connection, and disable windows firewall. (Clearly you would want to have the firewall setup to to allow connections, but on a private VM network you can take some allowances).

Preface 2: I am fully aware the agent validation is missing and this is not necessarily the most secure way of transmitting and ensuring the integrity of the agents. This provides the ground work and most security and authentication can be bolted in as needed.




Automated Agents - that term conjures a spiraling level of thought and ideas. From semi futuristic movies (Matrix, Terminators, Screamers, etc), super complex code, and a bit of fear for of lost control. The truth be told they are not too difficult to create when you decide to build an environment and platform for the agents to exist and interact. Think of it as building a game world for NPCs to take form in.

Quite a few years back the fringe business world was buzzing with the idea of having digital assistants at your beck and call - programmed to smartly know how to interact with various industries and systems (on your behalf) to purchase, research, and be an army of support for your lives. Purchasing groceries, airline tickets, stock research, and so forth.

Cutting down this concept to the core I decided a good example would be a simple auction house. The idea is there would be a one host system where the auction house would reside. The host would deal with item creation, collecting the agents for bidding, and created a standard interface for how to bid on an item.

On the flip side there would be a client system where people created agents, dictated their bidding behavior, and send them backing to the auction house. The agents would be allowed to determine how to bid as they wanted (running statistics on the bids, variable increments, etc), but they had to conform to the interface with the market place life was good.

Granted the agents in this tutorial are not crunching big numbers the key aspect is: they are deciding things independent of a person telling them to. With a bit of variable data the object operates without supervision and can produce different results depending on how other agents work with it. Complexity can crop up with the most simple rules!

On face this seems like a complex project, but with the .NET framework much of the tedious behavior is swept out of the way. How do we strap connections from the client to the host? The System.Net.Sockets namespace has TCPClient to do that! How would we create an object on one system and send it to another? Serialization and the System.Runtime.Serialization.Formatters' Binary.BinaryFormatter! A bit of extra code to puzzle out a menu to interact with our object and you are mostly there!

Conceptually here is what is going on:
Attached Image

Action wise - here are the important bits for the flow and state:
Attached Image

Let's dive into the code and see what is going on.

Agent.Agent

'-- A simple class to hold critical information the agent needs to know to do its job.
'-- Notice the class is serializable.. that means it can be broken down, thrown into a stream and moved to a disk or even another application.
<Serializable()>
Public Class Agent
...
    '-- The universal 'bidding'.. could be tweaked to be more robust.
    '-- a -1 return means drop out of the bidding else return how much you want to jump the price.
    Public Function DoBid(ByVal price As Int32) As Int32
        If price + Increment <= MaxBid Then
            Return Increment
        Else
            Return -1
        End If
    End Function
...
End Class



First - the agent. The critical take away is with one single line (<Serializable()>) we told Visual Studios to be ready to break down this object, and the data in that specific instance, to a serializable format. Binary, AScii, or what ever we chose. In theory we could save it with a stream writer to a file, or convert to a binary array for database storage. What this means you can take your object, freeze dry it, transfer/store it, and rehydrate the object in another time or place!

Also check out the bidding system - not that complex, but it provides variable results when combined with similar other agents!

Agent.Item

The item class is not particularly interesting - a filler class to hold an id, price, and a quick and dirty method to randomize the information for testing.

IntelligentAgent_Client

The client module has a few interesting parts. First is sending the agent across the wire.

  '-- sends the agent to the host.. if it was good then return true so the client can listen for the agent coming back.
    Private Function DoSending(ByVal serverIP As String, ByVal _agent As Agent.Agent) As Boolean
        Dim bReturn As Boolean = False

        Dim port As Int32 = 13000 '-- we are all connecting to this port on the host.. the host doesn't care who sends it an agent but if you do send it to this door.
        Dim client As TcpClient '-- the simple client connection to the host.
        Dim format As IFormatter '-- format the serialized agent to be sent across the wire.
        Dim stream As NetworkStream '-- the stream to feed the bits to for the host.
        Try
            '-- Create a TcpClient to our host on the given port.
            'http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.aspx
            client = New TcpClient(serverIP, port)

            '-- for the agent object to be serialized into binary to be sent across the stream to the host.
            'http://msdn.microsoft.com/en-us/library/system.runtime.serialization.formatters.binary.binaryformatter.aspx
            format = New BinaryFormatter()

            '-- the client's network stream to send the serialized agent.. much like writing to a filestream or streamwriter.
            'http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.getstream.aspx
            stream = client.GetStream()

            Console.WriteLine(String.Format("Sending agent: {0}   {1}", _agent.MYID, _agent.Increment))
            '-- take the agent, serialize it, and push it into the stream.
            format.Serialize(stream, _agent)

            '-- wait for the return to say it was a good send.
            bReturn = CType(format.Deserialize(stream), Boolean)
            Console.WriteLine(String.Format("Response: {0}", bReturn))

        Catch e As ArgumentNullException
            Console.WriteLine("ArgumentNullException: {0}", e)
            bReturn = False
        Catch e As SocketException
            Console.WriteLine("SocketException: {0}", e)
            bReturn = False
        Finally
            ' Close everything.
            If stream IsNot Nothing Then
                stream.Close()
                stream.Dispose()
            End If

            If client IsNot Nothing Then client.Close()
        End Try

        Return bReturn
    End Function



In the method we take an instance of the agent.agent class, serialize it to binary, and push it across a TCPClient connection to the host. This could easily mimic what I would do to write the agent to disk, or push it to a memory stream. When the agent is sent across we wait for a response back from the host if it was a good send (there might have been corruption or missing information). Here you could bolt in a retransmission or some sort of checker, but for this basic foundation that would clutter things.

Once we got the green light the agent was sent correctly the client then moves into a "wait for the agent's return" state. The client is essentially locked until the agent it sent comes home.

    '-- Similar to sending an agent, but instead listen to another port for any information coming down the stream (from the host).
    Private Function AgentReturn() As Boolean
        Dim bReturn As Boolean = False

        Dim server As TcpListener
        Dim port As Int32 = 13001 '-- don't listen to the port we were sending the agent on.
        Dim localAddr As IPAddress = IPAddress.Parse(_homeIP) '_hostIP
        Dim client As TcpClient
        Dim stream As NetworkStream
        Dim _agent As Agent.Agent
        Dim format As IFormatter

        Try
            '-- Just listen to our port until something comes in.. halts the program until it does.
            'http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx
            server = New TcpListener(localAddr, port)

            '-- start listening
            server.Start()

            ''   While True
            Console.Write("Waiting for a connection... ")

            '-- when something does come in shift it to the client.
            client = server.AcceptTcpClient()
            Console.WriteLine("Connected!")


            ' get the stream to read it.
            stream = client.GetStream()

            '-- the agent will be in binary so get ready for that.
            format = New BinaryFormatter()

            '-- deserialize and cast it as the agent.
            _agent = CType(format.Deserialize(stream), Agent.Agent)

            '-- send back to the host we got the object.
            format.Serialize(stream, _agent IsNot Nothing)

            Console.WriteLine(String.Format("Made it back: {0}", _agent.MYID))
            '-- set the global agent object to this new one gotten pack.
            _myAgent = _agent

            '-- see if we won an item!
            If _myAgent.ItemID = Guid.Empty Then
                Console.WriteLine("No item won... :(/>")
            Else
                Console.WriteLine(String.Format("Won item {0} at price {1}", _myAgent.ItemID, _myAgent.ItemPrice))
            End If

        Catch e As SocketException
            Console.WriteLine("SocketException: {0}", e)
        Finally
            '-- clean up
            server.Stop()
            If client IsNot Nothing Then client.Close()
        End Try

        Return bReturn
    End Function



Here the client listens to second port for data to come streaming in. It uses a TCPListener and blocks program actions until something shows up. The method then attempts to create the agent object, and sends the results back to the host. The stream here works like the 'Sending' method - data is transmitted in binary across a connection and the tcplistener jumps into action to collect and make sense of the data.

Again - if the agent wasn't re-hydrated correctly the host could try a retransmission, but that isn't implemented here.


IntelligentAgent_Host

The host works on the same principles of the client - using the TCPClient class to wait for agent data to stream in, tries to creates the agent, responds accordingly, and if an agent is created add to the queue of agents already sent.

The interesting part here is the auction the agents operate in.

'-- for the auction we wait for the agents.. then start the round robin bidding.
    Private Sub DoAuction()

        If _homeIP = String.Empty Then
            Console.WriteLine("Set Host IP!")
            Exit Sub
        End If

        '-- kill off any agents still around
        If _agentList.Count > 0 Then _agentList.Clear()

        '-- wait for the agents
        WaitForAgents()

        '-- the actual bidding.
        '-- this gist here is of all the agents are in a list.. if they drop out because of a max bid they are off the list.
        '-- go until the list is empty or has one agent.
        Dim activeBidders As New List(Of Agent.Agent)(_agentList)

        Dim lCurrentPrice As Int32 = _item.Price
        Dim tempBid As Int32 = 0

        Console.WriteLine("Auction Starting")
        Console.WriteLine(_item.ToString)
        Console.WriteLine("--------------")

        '-- keep the agents going round right round.
        While activeBidders.Count > 1
            '-- displays to the host what happened.. might as well be logging to a file or a db.
            Console.WriteLine(String.Format("Current Price: {0}", lCurrentPrice))
            For i As Int32 = activeBidders.Count - 1 To 0 Step -1
                tempBid = activeBidders(i).DoBid(lCurrentPrice)
                '-- deal with what the agents said.
                If tempBid = -1 Then
                    Console.WriteLine(String.Format("{0} dropped out.", activeBidders(i).MYID))
                    activeBidders.Remove(activeBidders(i))
                Else
                    If activeBidders.Count > 1 Then
                        lCurrentPrice += tempBid
                        Console.WriteLine(String.Format("{0}: {1}.", activeBidders(i).MYID, lCurrentPrice))
                    End If
                End If
            Next
        End While

        '-- if everyones out the item isn't won.
        If activeBidders.Count = 0 Then
            Console.WriteLine("No winners")
        Else
            '-- if won then send the agent with the item id and how much
            If activeBidders.Count = 1 Then
                For Each temp As Agent.Agent In _agentList
                    If temp.MYID = activeBidders(0).MYID Then
                        temp.ItemID = _item.ID
                        temp.ItemPrice = lCurrentPrice
                        Console.WriteLine(String.Format("Agent {0} won item {1} at price {2}({3})", temp.MYID, temp.ItemID, temp.ItemPrice, _item.Price))
                        Exit For

                    End If
                Next
            End If
        End If
        
        '-- send everyone home.
        For Each temp As Agent.Agent In _agentList
            SendAgentBack(temp)
        Next

    End Sub



Here the host waits to collect the agents, shifts them into a new collection on who is active or not, and does a simple while loop to bid on an object. The gist is for each agent the current price is sent in. The agent acts on this data and either sends back the increment it wants to provide or -1 to say it is out of the auction (and removed from the list of active bidders).

When there is one or no agents left the item is dolled out (or not) and then the host sends all the agents back to their home systems. The sending is pretty much the same code as the agent sending to the host! A good bit of parity of functions between the host and client.

In a nutshell - that is it! There's some filler code for creating a more user friend menu system and setting uninteresting data, but the meat of the system: transmitting agents to another system and having them interact is there. The host's "DoAuction" provides a common arena for the agent objects to meet up, a semi circular loop to keep a baseline to act, and a common interface to have each agent decide on the data at hand.

The scary big idea of "how to get my object into binary" is wrapped up in what - eight lines of code, and creating a connection between two systems (on different IPs) boils down to about as many lines?

Advanced topics:
  • making agents smarter or more complex
    • sliding biding increments
    • waiting for right item

  • multi item biding - so agents go in with a list of items to wait/bid for - and not just all of them.
  • a better way of threading the bidding process so it is not a queue
  • security of transmission and ensuring object integrity
  • agents bring items to be bid on
  • a more robust way to track money and transactions with a database.


Project setup:

My solution structure:
Attached Image

Code:

Agent.Agent
Spoiler

Agent.Item
Spoiler

IntelligentAgent_Client
Spoiler

IntelligentAgent_Host
Spoiler


Example of interaction:
Spoiler


Is This A Good Question/Topic? 5
  • +

Page 1 of 1