source: geniuswebcore/geniusweb/protocol/session/mopac/PartyStates.py@ 77

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

Fix for IssueValue hashcode.

File size: 7.3 KB
Line 
1from typing import Set, List, Dict, TypeVar, Optional
2
3from geniusweb.actions.Action import Action
4from geniusweb.actions.EndNegotiation import EndNegotiation
5from geniusweb.actions.PartyId import PartyId
6from geniusweb.inform.Agreements import Agreements
7from geniusweb.protocol.ProtocolException import ProtocolException
8from geniusweb.utils import toTuple, val, toStr
9
10
11class PartyStates:
12 '''
13 Invariant: contains the current state of all participating parties. A party
14 either notYetActed, did action, reached an agreement, walked away or made a
15 {@link ProtocolException}.
16 <p>
17 After a phase completes, the actions are filtered/handled to collect
18 agreements and move remaining parties back to notYetActed.
19 '''
20 # all parties must be in exactly one state at all times.
21
22 def __init__(self, powers:Dict[PartyId, int],
23 notYetActed:Optional[Set[PartyId]]=None,
24 actions: List[Action] =[],
25 agreements:Agreements = Agreements(),
26 walkedAway:List[PartyId]=[],
27 exceptions:Dict[PartyId, ProtocolException] ={}):
28 '''
29 notice: order of arguments, power is now first instead of last because
30 python requires obligatory arguments first.
31 '''
32 if notYetActed!=None:
33 self._notYetActed = set(notYetActed) #type:ignore
34 else:
35 self._notYetActed = set(powers.keys())
36 self._actions = list(actions)
37 self._agreements = agreements
38 self._walkedAway = list(walkedAway)
39 self._exceptions = dict(exceptions)
40 self._powers = dict(powers)
41
42
43
44 def WithAction(self, action:Action)-> "PartyStates":
45 '''
46 @param action the action done by some party. The correctness of action,
47 particularly of {@link Action#getActor()}, must be correct.
48 @return new state with party done given action. This just accepts any
49 given action and is not considering number of rounds, time or
50 whether an action is allowed.
51
52 @throws IllegalArgumentException if party doing action already acted.
53 This is just a safety check as legality
54 of actions should be checked before
55 calling this.
56 '''
57 if not action.getActor() in self._notYetActed:
58 raise ValueError("actor already acted: " + str(action))
59 if isinstance(action, EndNegotiation):
60 return self.WithWalkAway(action.getActor())
61 newActions = list(self._actions)
62 newActions.append(action)
63 return PartyStates(self._powers, self._removeParty(action.getActor()), newActions,
64 self._agreements, self._walkedAway, self._exceptions)
65
66 def WithAgreements(self, newAgree:Agreements )->"PartyStates" :
67 '''
68 @param newAgree a new agreements to be merged with existing agreements.
69 The parties in the agreement must have acted and thus in
70 actions.
71 @return new PartyStates with agreeing parties removed from actions and
72 added to Agreements.
73 '''
74 parties = newAgree.getMap().keys()
75 newActions = [act for act in self._actions if not act.getActor() in parties]
76 return PartyStates(self._powers, self._notYetActed, newActions,
77 self._agreements.With(newAgree), self._walkedAway, self._exceptions)
78
79
80 def WithWalkAway(self, actor:PartyId ) ->"PartyStates":
81 if not actor in self._notYetActed:
82 raise ValueError("actor already acted: " + str(actor))
83 newWalkAway = list(self._walkedAway)
84 newWalkAway.append(actor)
85 return PartyStates(self._powers,self._removeParty(actor), self._actions, self._agreements,
86 newWalkAway, self._exceptions)
87
88 def WithException(self, e:ProtocolException ) -> "PartyStates":
89 '''
90 Move party from active to exceptions.
91
92 @param e the exception that the party caused
93 @return new PartyStates with party that caused the exception in the
94 exceptions list and removed from the active list. Nothing happens
95 if party is not active.
96 '''
97 if not e.getParty() in self._notYetActed:
98 # complex case. Party did valid action but now is messing around.
99 # Easiest seems to completely ignore it. CHECK.
100 return self;
101
102 newExc = dict(self._exceptions)
103 newExc[val(e.getParty())]= e
104 return PartyStates(self._powers, self._removeParty(val(e.getParty())),
105 self._actions, self._agreements,self._walkedAway, newExc)
106
107 def getNotYetActed(self)->Set[PartyId]:
108 '''
109 @return parties that have not yet acted
110
111 '''
112 return self._notYetActed
113
114 def getNegotiatingParties(self)-> Set[PartyId]:
115 '''
116 @return parties that are still in the negotiation. These are the parties
117 that not yet acted plus the ones that did an action.
118 '''
119 parties = set([ act.getActor() for act in self._actions ])
120
121 parties = parties.union(self._notYetActed)
122 return parties
123
124 def _removeParty(self, party:PartyId )->Set[PartyId] :
125 '''
126 @param actor
127 @return {@link #notYetActed} with actor removed
128 @throws IllegalArgumentException if party already acted (not in
129 {@link #notYetActed}).
130 '''
131 newActiveParties = set(self._notYetActed)
132 if not party in newActiveParties:
133 raise ValueError(
134 "Party " + str(party) + " is not active, can't be removed")
135 newActiveParties.remove(party)
136 return newActiveParties
137
138 def getAgreements(self)->Agreements :
139 return self._agreements
140
141 def getPowers(self)->Dict[PartyId,int] :
142 return dict(self._powers)
143
144 def getExceptions(self)->Dict[PartyId, ProtocolException]:
145 return dict(self._exceptions)
146
147 def finish(self)->"PartyStates":
148 '''
149 @return new state where all {@link #notYetActed} are moved to the
150 exceptions list.
151 '''
152 newstate = self
153 for party in self._notYetActed:
154 newstate = newstate.WithException(ProtocolException("Party did not act", party))
155 return newstate
156
157 def getActions(self)->List[Action] :
158 return list(self._actions)
159
160 def getWalkedAway(self)->List[PartyId] :
161 '''
162 @return all parties that walked away
163 '''
164 return list(self._walkedAway)
165
166# T=TypeVar("T", Action)
167 def getActionsOfType(self, typ) -> List:
168 '''
169 @param <T> the type of objects requested
170 @param typ the type of actions to extract. Must be of type T, needed
171 because of java's type erasure.
172
173 @return all actions of requested type in this phase
174 '''
175 return [act for act in self._actions if isinstance(act, typ) ]
176
177 def __repr__(self):
178 return "PartyStates[" + str(list(self._notYetActed)) + "," + str(self._actions) + "," + \
179 str(self._agreements)+ "," + str(self._walkedAway) + "," + toStr(self._exceptions) + "]"
180
181 def flush(self)->"PartyStates" :
182 '''
183 @return states with all parties that are in {@link #actions} moved back
184 to {@link #notYetActed}. This state is then ready for a next
185 phase.
186 @throws IllegalStateException if {@link #notYetActed} is not empty
187 '''
188 if len(self._notYetActed)!=0:
189 raise ValueError(
190 "Some parties did not yet act:" + str(self._notYetActed))
191 return PartyStates(self._powers, self.getNegotiatingParties(), [],
192 self._agreements, self._walkedAway, self._exceptions)
193
194 def __hash__(self):
195 return hash((tuple(self._actions), self._agreements, toTuple(self._exceptions),
196 tuple(self._notYetActed), toTuple(self._powers), tuple(self._walkedAway)))
197
198 def __eq__(self, other):
199 return isinstance(other, self.__class__)\
200 and self._actions == other._actions \
201 and self._agreements == other._agreements \
202 and self._exceptions == other._exceptions \
203 and self._notYetActed == other._notYetActed \
204 and self._powers == other._powers \
205 and self._walkedAway == other._walkedAway
206
Note: See TracBrowser for help on using the repository browser.