| 35 | == Writing a party |
| 36 | Example parties can be found [source:/exampleparties here]. You can easily clone a party with SVN using {{{svn co https://tracinsy.ewi.tudelft.nl/pub/svn/GeniusWebPython/exampleparties/randomparty/}}} (this clones randomparty, use a different name to fetch another example). |
| 37 | |
| 38 | After cloning, create your venv and install the requirements, eg |
| 39 | {{{ |
| 40 | python3 -m venv venv |
| 41 | source venv/bin/activate |
| 42 | pip install -r requirements.txt |
| 43 | }}} |
| 44 | |
| 45 | You can now run the junit tests. |
| 46 | |
| 47 | You may have to take different or additional steps if you use an IDE like eclipse with pydev, or pycharm. |
| 48 | |
| 49 | A party is compiled with python. Compile with {{{python3 setup.py sdist}}}. This gives you a you get a {{{dist/yourparty-X.Y.Z.tar.gz}}} that can be copied into the pypartiesserver for deployment. |
| 50 | |
| 51 | The basic structure of a party that supports the SAOP behaviour looks like this |
| 52 | {{{ |
| 53 | class RandomParty (DefaultParty): |
| 54 | """ |
| 55 | Offers random bids until a bid with sufficient utility is offered |
| 56 | """ |
| 57 | def __init__(self): |
| 58 | super().__init__() |
| 59 | self.getReporter().log(logging.INFO,"party is initialized") |
| 60 | self._profile = None |
| 61 | self._lastReceivedBid:Bid = None |
| 62 | |
| 63 | def notifyChange(self, info: Inform): |
| 64 | #self.getReporter().log(logging.INFO,"received info:"+str(info)) |
| 65 | if isinstance(info,Settings) : |
| 66 | settings:Settings=cast(Settings,info) |
| 67 | self._me = settings.getID() |
| 68 | self._protocol:str = str(settings.getProtocol().getURI()) |
| 69 | self._progress = settings.getProgress() |
| 70 | if "Learn" == self._protocol: |
| 71 | self.getConnection().send(LearningDone(self._me)) |
| 72 | else: |
| 73 | self._profile = ProfileConnectionFactory.create(info.getProfile().getURI(), self.getReporter()) |
| 74 | elif isinstance(info, ActionDone): |
| 75 | action:Action=cast( ActionDone,info).getAction() |
| 76 | if isinstance(action, Offer): |
| 77 | self._lastReceivedBid = cast(Offer, action).getBid() |
| 78 | elif isinstance(info, YourTurn): |
| 79 | self._myTurn() |
| 80 | if isinstance(self._progress, ProgressRounds) : |
| 81 | self._progress = self._progress.advance() |
| 82 | elif isinstance(info, Finished): |
| 83 | self.terminate() |
| 84 | else: |
| 85 | self.getReporter().log(logging.WARNING, "Ignoring unknown info "+str(info)) |
| 86 | |
| 87 | |
| 88 | def getCapabilities(self) -> Capabilities: |
| 89 | return Capabilities( set([ "SAOP", "Learn"]), set(['geniusweb.profile.utilityspace.LinearAdditive'])) |
| 90 | |
| 91 | def getDescription(self) -> str: |
| 92 | return "Offers random bids until a bid with sufficient utility is offered" |
| 93 | |
| 94 | def _myTurn(self): |
| 95 | if self._lastReceivedBid != None and \ |
| 96 | self._profile.getProfile().getUtility(self._lastReceivedBid) > 0.6: |
| 97 | action = Accept(self._me, self._lastReceivedBid) |
| 98 | else: |
| 99 | for _attempt in range(20): |
| 100 | bid = self._getRandomBid(self._profile.getProfile().getDomain()) |
| 101 | if self._isGood(bid): |
| 102 | break |
| 103 | action = Offer(self._me, bid); |
| 104 | self.getConnection().send(action) |
| 105 | |
| 106 | def _isGood(self, bid:Bid)->bool: |
| 107 | profile = self._profile.getProfile() |
| 108 | return profile.getUtility(bid) > 0.6 |
| 109 | |
| 110 | ... |
| 111 | }}} |
| 112 | |
| 113 | The party must follow the behaviours that it promises. The example above folows the SAOP behaviour. |
| 114 | |
| 115 | Notice, above is slightly simplified code, for fully working code check the [source:/exampleparties/randomparty/src/main/java/geniusweb/exampleparties/randomparty/RandomParty.java source code]. |
| 116 | |
| 117 | === Throwing exceptions |
| 118 | A special warning is in place regarding throwing exceptions from your implementation, particularly from notifyChange. In line with the general Listener interface, exceptions are only logged and your party usually is immediately disconnected if it throws any exception. Make sure that you free up any used resources to avoid memory leaks if you really need to throw an exception. |
| 119 | |
| 120 | === Preparing the jar file |
| 121 | In order to put your party on the [https://tracinsy.ewi.tudelft.nl/pubtrac/GeniusWebPartiesServer partiesserver] for running, you need a jar file of your party. |
| 122 | |
| 123 | Normally the party includes all dependencies. The jar files are loaded with an isolated jar class loader that should avoid collisions with possibly identically named but possibly different packages in other jar files. |
| 124 | |
| 125 | |
| 126 | Party jar files must have a Main-Class set in the MANIFEST.MF file. This main-class must implement Party and have a no-arg constructor. |
| 127 | |
| 128 | The example randomparty does this from the maven build script. |
| 129 | |
| 130 | We recommend to do initialization of the party only in the init() and not in the constructor or static code. |
| 131 | This because instances of your class can be made both for extracting general info as getDescription(), or to really run your class. |
| 132 | |
| 133 | |