source: geniuswebcore/geniusweb/protocol/session/saop/SAOPState.py@ 67

Last change on this file since 67 was 67, checked in by Bart Vastenhouw, 3 years ago

Added SAOP and simplerunner to GeniusWebPython. Several minor fixes.

File size: 5.5 KB
Line 
1from __future__ import annotations
2
3from typing import List, Dict, Optional, TYPE_CHECKING
4
5from geniusweb.actions.Accept import Accept
6from geniusweb.actions.Action import Action
7from geniusweb.actions.EndNegotiation import EndNegotiation
8from geniusweb.actions.Offer import Offer
9from geniusweb.actions.PartyId import PartyId
10from geniusweb.inform.Agreements import Agreements
11from geniusweb.issuevalue.Bid import Bid
12from geniusweb.progress.Progress import Progress
13from geniusweb.progress.ProgressRounds import ProgressRounds
14from geniusweb.protocol.ProtocolException import ProtocolException
15from geniusweb.protocol.session.DefaultSessionState import DefaultSessionState
16from geniusweb.protocol.session.SessionResult import SessionResult
17from geniusweb.protocol.session.saop.SAOPSettings import SAOPSettings
18from geniusweb.references.PartyWithProfile import PartyWithProfile
19
20#CHECK WHY DO THE TYPE VARIABLES HAVE TO BE SWAPPED HERE?
21class SAOPState (DefaultSessionState[ "SAOPSettings","SAOPState"]):
22 '''
23 Immutable.
24 '''
25
26 def __init__(self, actions:List[Action] , connections:List[PartyId] ,
27 progress:Optional[Progress] , settings: SAOPSettings ,
28 partyprofiles:Dict[PartyId, PartyWithProfile]={},
29 error:Optional[ProtocolException]=None) :
30 '''
31
32 @param actions the actions done by the parties
33 @param conns the existing party connections. If null, default
34 empty list is used. Can be less than 2 parties in
35 the first phases of the setup or after parties
36 disconnected.
37 @param progress the {@link Progress} line. can be null
38 @param settings the {@link SAOPSettings}
39 @param partyprofiles map with the {@link PartyWithProfile} for connected
40 parties. null is equivalent to an empty map.
41 @param e the {@link ProtocolException}, or null if none
42 occurred.
43 '''
44 super().__init__(actions, connections, progress, settings, partyprofiles, error)
45
46 def With(self, actions:List[Action] , conns: List[PartyId],
47 progr:Optional[Progress] , settings: SAOPSettings,
48 partyprofiles:Dict[PartyId, PartyWithProfile] , e:Optional[ProtocolException]) -> "SAOPState" :
49 assert isinstance(actions, list)
50 #assert isinstance(settings, SAOPSettings)
51 assert isinstance(partyprofiles, dict)
52 return SAOPState(actions, conns, progr, settings, partyprofiles, e);
53
54
55 def getAgreements(self) ->Agreements :
56 agree = Agreements()
57 acts = self.getActions()
58 nparticipants = len(self.getConnections())
59 if nparticipants < 2 or len(acts) < nparticipants:
60 return agree;
61 offer = acts[len(acts) - nparticipants]
62 if not isinstance(offer, Offer):
63 return agree;
64 bid = offer.getBid()
65
66 # check that the last n-1 are accepts.
67 allaccept = all( [ (isinstance(act, Accept) and bid==act.getBid())\
68 for act in acts[-(nparticipants-1):]] )
69 if allaccept:
70 agree = Agreements({party: bid for party in self._getParties()})
71 return agree
72
73 def _getParties(self)->List[PartyId] :
74 '''
75 @return all currently connected parties.
76 '''
77 return self.getConnections()
78
79 def isFinal(self, currentTimeMs:int)->bool:
80 acts = self.getActions()
81 return super().isFinal(currentTimeMs) \
82 or self.getAgreements().getMap()!={} \
83 or (not acts==[] and isinstance(acts[- 1], EndNegotiation))
84
85 def WithAction(self, actor:PartyId , action:Action ) -> "SAOPState":
86 return super().WithAction(actor, action).WithProgress(self.advanceProgress())
87
88 def checkAction(self, actor:PartyId, action:Action ) -> Optional[str]:
89 msg = super().checkAction(actor, action)
90 if msg:
91 return msg
92
93 if actor != self._getNextActor():
94 return "Party does not have the turn "
95
96 # check protocol is followed for specific actions
97 if isinstance(action, Accept):
98 bid = self._getLastBid()
99 if not bid:
100 return "Accept without a recent offer";
101
102 if bid != action.getBid():
103 return "Party accepts a bid differing from the last offer ="\
104 + str(bid) + ", action=" + str(action) + ")"
105 return None
106 elif isinstance(action,Offer):
107 return None
108 elif isinstance(action, EndNegotiation):
109 return None
110 return "Action " + str(action) + " is not allowed in SAOP"
111
112
113 def _getLastBid(self)-> Optional[ Bid ]:
114 '''
115 Check up to nparticipants-1 steps back if there was an offer.
116
117 @return Bid from the most recent offer, or null if no such offer
118 '''
119 nparticipants = len(self.getConnections())
120 acts = self.getActions()
121 n = len(acts) - 1
122 while n > len(acts) - nparticipants and n >= 0:
123 action = acts[n]
124 if isinstance(action ,Offer):
125 return action.getBid()
126 n=n-1
127 return None
128
129 def _getNextActor(self)->PartyId :
130 '''
131 @return the next actor in the current state. Assumes 1 action per actor
132 every time. NOTE if party disconnects this would jump wildly but
133 nego stops then anyway
134 '''
135 return self.getConnections()[len(self.getActions()) % len(self.getConnections())]
136
137 def advanceProgress(self)-> Progress :
138 '''
139 @return new progress state
140 '''
141 newprogress = self.getProgress()
142 assert newprogress
143 if isinstance(newprogress,ProgressRounds) and self._isLastActor():
144 newprogress = newprogress.advance()
145 return newprogress
146
147 def _isLastActor(self) -> bool:
148 '''
149 @return true if the current actor is the last actor in the list
150 '''
151 nparticipants = len(self.getConnections())
152 return len(self.getActions()) % nparticipants == nparticipants - 1;
153
154 def getResult(self) -> SessionResult:
155 return SessionResult(self.getPartyProfiles(), self.getAgreements(),
156 {}, self.getError())
Note: See TracBrowser for help on using the repository browser.