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

Last change on this file was 100, checked in by ruud, 21 months ago

python installs also wheel to avoid error messages

File size: 5.4 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
21class SAOPState (DefaultSessionState[ "SAOPState", "SAOPSettings"]):
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 def getAgreements(self) -> Agreements:
55 agree = Agreements()
56 acts = self.getActions()
57 nparticipants = len(self.getConnections())
58 if nparticipants < 2 or len(acts) < nparticipants:
59 return agree;
60 offer = acts[len(acts) - nparticipants]
61 if not isinstance(offer, Offer):
62 return agree;
63 bid = offer.getBid()
64
65 # check that the last n-1 are accepts.
66 allaccept = all([ (isinstance(act, Accept) and bid == act.getBid())\
67 for act in acts[-(nparticipants - 1):]])
68 if allaccept:
69 agree = Agreements({party: bid for party in self._getParties()})
70 return agree
71
72 def _getParties(self) -> List[PartyId]:
73 '''
74 @return all currently connected parties.
75 '''
76 return self.getConnections()
77
78 def isFinal(self, currentTimeMs:int) -> bool:
79 acts = self.getActions()
80 return super().isFinal(currentTimeMs) \
81 or self.getAgreements().getMap() != {} \
82 or (not acts == [] and isinstance(acts[-1], EndNegotiation))
83
84 def WithAction(self, actor:PartyId , action:Action) -> "SAOPState":
85 return super().WithAction(actor, action).WithProgress(self.advanceProgress())
86
87 def checkAction(self, actor:PartyId, action:Action) -> Optional[str]:
88 msg = super().checkAction(actor, action)
89 if msg:
90 return msg
91
92 if actor != self._getNextActor():
93 return "Party does not have the turn "
94
95 # check protocol is followed for specific actions
96 if isinstance(action, Accept):
97 bid = self._getLastBid()
98 if not bid:
99 return "Accept without a recent offer";
100
101 if bid != action.getBid():
102 return "Party accepts a bid differing from the last offer ="\
103 +str(bid) + ", action=" + str(action) + ")"
104 return None
105 elif isinstance(action, Offer):
106 return None
107 elif isinstance(action, EndNegotiation):
108 return None
109 return "Action " + str(action) + " is not allowed in SAOP"
110
111 def _getLastBid(self) -> Optional[ Bid ]:
112 '''
113 Check up to nparticipants-1 steps back if there was an offer.
114
115 @return Bid from the most recent offer, or null if no such offer
116 '''
117 nparticipants = len(self.getConnections())
118 acts = self.getActions()
119 n = len(acts) - 1
120 while n > len(acts) - nparticipants and n >= 0:
121 action = acts[n]
122 if isinstance(action , Offer):
123 return action.getBid()
124 n = n - 1
125 return None
126
127 def _getNextActor(self) -> PartyId:
128 '''
129 @return the next actor in the current state. Assumes 1 action per actor
130 every time. NOTE if party disconnects this would jump wildly but
131 nego stops then anyway
132 '''
133 return self.getConnections()[len(self.getActions()) % len(self.getConnections())]
134
135 def advanceProgress(self) -> Progress:
136 '''
137 @return new progress state
138 '''
139 newprogress = self.getProgress()
140 assert newprogress
141 if isinstance(newprogress, ProgressRounds) and self._isLastActor():
142 newprogress = newprogress.advance()
143 return newprogress
144
145 def _isLastActor(self) -> bool:
146 '''
147 @return true if the current actor is the last actor in the list
148 '''
149 nparticipants = len(self.getConnections())
150 return len(self.getActions()) % nparticipants == nparticipants - 1;
151
152 def getResults(self) -> List[SessionResult]:
153 return [SessionResult(self.getPartyProfiles(), self.getAgreements(),
154 {}, self.getError())]
Note: See TracBrowser for help on using the repository browser.