package geniusweb.exampleparties.randomparty;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.logging.Level;
import java.util.stream.Collectors;
import geniusweb.actions.Accept;
import geniusweb.actions.Action;
import geniusweb.actions.LearningDone;
import geniusweb.actions.Offer;
import geniusweb.actions.PartyId;
import geniusweb.actions.Vote;
import geniusweb.actions.VoteWithValue;
import geniusweb.actions.Votes;
import geniusweb.actions.VotesWithValue;
import geniusweb.bidspace.AllPartialBidsList;
import geniusweb.inform.ActionDone;
import geniusweb.inform.Finished;
import geniusweb.inform.Inform;
import geniusweb.inform.OptIn;
import geniusweb.inform.OptInWithValue;
import geniusweb.inform.Settings;
import geniusweb.inform.Voting;
import geniusweb.inform.YourTurn;
import geniusweb.issuevalue.Bid;
import geniusweb.party.Capabilities;
import geniusweb.party.DefaultParty;
import geniusweb.profile.PartialOrdering;
import geniusweb.profile.Profile;
import geniusweb.profile.utilityspace.UtilitySpace;
import geniusweb.profileconnection.ProfileConnectionFactory;
import geniusweb.profileconnection.ProfileInterface;
import geniusweb.progress.Progress;
import geniusweb.progress.ProgressRounds;
import tudelft.utilities.logging.Reporter;
/**
* A party that places random bids and accepts when it receives an offer with
* sufficient utility. This party is also a demo on how to support the various
* protocols, which causes this party to look a bit complex.
*
*
parameters
*
* parameters
*
* minPower |
* This value is used as minPower for placed {@link Vote}s. Default value is
* 2. |
*
*
* maxPower |
* This value is used as maxPower for placed {@link Vote}s. Default value is
* infinity. |
*
*
*/
public class RandomParty extends DefaultParty {
private Bid lastReceivedBid = null;
private PartyId me;
private final Random random = new Random();
protected ProfileInterface profileint = null;
private Progress progress;
private Settings settings;
private Votes lastvotes;
private VotesWithValue lastvoteswithvalue;
private String protocol;
public RandomParty() {
}
public RandomParty(Reporter reporter) {
super(reporter); // for debugging
}
@Override
public void notifyChange(Inform info) {
try {
if (info instanceof Settings) {
Settings sett = (Settings) info;
this.me = sett.getID();
this.progress = sett.getProgress();
this.settings = sett;
this.protocol = sett.getProtocol().getURI().getPath();
if ("Learn".equals(protocol)) {
getConnection().send(new LearningDone(me));
} else {
this.profileint = ProfileConnectionFactory
.create(sett.getProfile().getURI(), getReporter());
}
} else if (info instanceof ActionDone) {
Action otheract = ((ActionDone) info).getAction();
if (otheract instanceof Offer) {
lastReceivedBid = ((Offer) otheract).getBid();
}
} else if (info instanceof YourTurn) {
makeOffer();
} else if (info instanceof Finished) {
getReporter().log(Level.INFO, "Final ourcome:" + info);
terminate(); // stop this party and free resources.
} else if (info instanceof Voting) {
switch (protocol) {
case "MOPAC":
lastvotes = vote((Voting) info);
getConnection().send(lastvotes);
break;
case "MOPAC2":
lastvoteswithvalue = voteWithValue((Voting) info);
getConnection().send(lastvoteswithvalue);
}
} else if (info instanceof OptIn) {
// just repeat our last vote.
getConnection().send(lastvotes);
} else if (info instanceof OptInWithValue) {
getConnection().send(lastvoteswithvalue);
}
} catch (Exception e) {
throw new RuntimeException("Failed to handle info", e);
}
updateRound(info);
}
@Override
public Capabilities getCapabilities() {
return new Capabilities(new HashSet<>(
Arrays.asList("SAOP", "AMOP", "MOPAC", "MOPAC2", "Learn")),
Collections.singleton(Profile.class));
}
@Override
public String getDescription() {
return "places random bids until it can accept an offer with utility >0.6. "
+ "Parameters minPower and maxPower can be used to control voting behaviour.";
}
@Override
public void terminate() {
super.terminate();
if (this.profileint != null) {
this.profileint.close();
this.profileint = null;
}
}
/******************* private support funcs ************************/
/**
* Update {@link #progress}
*
* @param info the received info. Used to determine if this is the last info
* of the round
*/
private void updateRound(Inform info) {
if (protocol == null)
return;
switch (protocol) {
case "SAOP":
case "SHAOP":
if (!(info instanceof YourTurn))
return;
break;
case "MOPAC":
if (!(info instanceof OptIn))
return;
break;
case "MOPAC2":
if (!(info instanceof OptInWithValue))
return;
break;
default:
return;
}
// if we get here, round must be increased.
if (progress instanceof ProgressRounds) {
progress = ((ProgressRounds) progress).advance();
}
}
/**
* send our next offer
*/
private void makeOffer() throws IOException {
Action action;
if ((protocol.equals("SAOP") || protocol.equals("SHAOP"))
&& isGood(lastReceivedBid)) {
action = new Accept(me, lastReceivedBid);
} else {
// for demo. Obviously full bids have higher util in general
AllPartialBidsList bidspace = new AllPartialBidsList(
profileint.getProfile().getDomain());
Bid bid = null;
for (int attempt = 0; attempt < 20 && !isGood(bid); attempt++) {
long i = random.nextInt(bidspace.size().intValue());
bid = bidspace.get(BigInteger.valueOf(i));
}
action = new Offer(me, bid);
}
getConnection().send(action);
}
/**
* @param bid the bid to check
* @return true iff bid is good for us.
*/
private boolean isGood(Bid bid) {
if (bid == null)
return false;
Profile profile;
try {
profile = profileint.getProfile();
} catch (IOException e) {
throw new IllegalStateException(e);
}
if (profile instanceof UtilitySpace)
return ((UtilitySpace) profile).getUtility(bid).doubleValue() > 0.6;
if (profile instanceof PartialOrdering) {
return ((PartialOrdering) profile).isPreferredOrEqual(bid,
profile.getReservationBid());
}
return false;
}
/**
* @param voting the {@link Voting} object containing the options
*
* @return our next Votes.
*/
private Votes vote(Voting voting) throws IOException {
Object val = settings.getParameters().get("minPower");
Integer minpower = (val instanceof Integer) ? (Integer) val : 2;
val = settings.getParameters().get("maxPower");
Integer maxpower = (val instanceof Integer) ? (Integer) val
: Integer.MAX_VALUE;
Set votes = voting.getOffers().stream().distinct()
.filter(offer -> isGood(offer.getBid()))
.map(offer -> new Vote(me, offer.getBid(), minpower, maxpower))
.collect(Collectors.toSet());
return new Votes(me, votes);
}
/**
* @param voting the {@link Voting} object containing the options
*
* @return our next Votes. Returns only votes on good bids and tries to
* distribute vote values evenly over all good bids.
*/
private VotesWithValue voteWithValue(Voting voting) throws IOException {
Object val = settings.getParameters().get("minPower");
Integer minpower = (val instanceof Integer) ? (Integer) val : 2;
val = settings.getParameters().get("maxPower");
Integer maxpower = (val instanceof Integer) ? (Integer) val
: Integer.MAX_VALUE;
List goodbids = voting.getOffers().stream().distinct()
.filter(offer -> isGood(offer.getBid()))
.map(offer -> offer.getBid()).collect(Collectors.toList());
if (goodbids.isEmpty()) {
return new VotesWithValue(me, Collections.emptySet());
}
// extra difficulty now is to have the utility sum to exactly 100
int mostvalues = 100 / goodbids.size();
int value = 100 - mostvalues * (goodbids.size() - 1);
Set votes = new HashSet<>();
for (Bid bid : goodbids) {
votes.add(new VoteWithValue(me, bid, minpower, maxpower, value));
value = mostvalues;
}
return new VotesWithValue(me, votes);
}
}