PlayerID{{ public int compareTo(PlayerID pid) { return ((Integer)id).compareTo(pid.id); } }} Var{{ public int compareTo(Var v) { return id.compareTo(v.id); } }} Solution{{ /** Create a solution using a Map... now we use ListMap for display */ public Solution(Map map){ this(ListMap.create(map.toList())); } }} ChallengeKind{{ public static final All ALL = new All(); public static final Secret SECRET = new Secret(); public abstract boolean isSecret(); }} All{{ public boolean isSecret(){ return false; } }} Secret{{ public boolean isSecret(){ return true; } }} Challenge{{ /** Default challenge kind is ALL -- Commented to uncover potential bugs public Challenge(int key, PlayerID challenger, ProblemType pred, double price){ this(key, challenger, pred, price, ChallengeKind.ALL); } */ public boolean isSecret(){ return kind.isSecret(); } }} OfferedChallenge{{ public AcceptedChallenge accept(PlayerID challengee){ return new AcceptedChallenge(challengee, this.key, this.challenger, this.pred, this.price, this.kind); } public OfferedChallenge reoffer(PlayerID challenger, double newPrice){ return new OfferedChallenge(key, challenger, pred, newPrice, kind); } }} AcceptedChallenge{{ public ProvidedChallenge provide(Problem problem){ return new ProvidedChallenge(challengee, problem, key, challenger, pred, price, kind); } }} ProvidedChallenge{{ public SolvedChallenge solve(Solution sol, double qual){ return new SolvedChallenge(challengee, instance, sol, qual, key, challenger, pred, price, kind); } }} SolvedChallenge{{ }} Transaction{{ public int getChallengeid(){ throw new RuntimeException("No Challenge ID"); } public abstract Transaction install(int playerId, GameI game, boolean isOverTime); public boolean isOffer(){ return false; } public boolean isReoffer(){ return false; } public boolean isAccept(){ return false; } public boolean isProvide(){ return false; } public boolean isSolve(){ return false; } }} OfferTrans{{ /** Construct an OfferTrans with a default ID */ public OfferTrans(ChallengeKind kind, ProblemType typ, double prc){ this(0, kind, typ, prc); } /** Construct an OfferTrans with a default KIND */ public OfferTrans(ProblemType typ, double prc){ this(ChallengeKind.ALL, typ, prc); } public OfferTrans install(int playerId, GameI game, boolean isOverTime){ if(!isOverTime){ return game.installTransaction(playerId, this); } return this; } /** Update this OfferTrans to match the Challenge's Assigned ID public OfferTrans updateId(int id){ return new OfferTrans(id, pred, price); } */ public boolean isOffer(){ return true; } }} AcceptTrans{{ public AcceptTrans install(int playerId, GameI game, boolean isOverTime){ if(!isOverTime){ return game.installTransaction(playerId, this); } return this; } public boolean isAccept(){ return true; } }} ProvideTrans{{ /** Provide an ALL challenge*/ public ProvideTrans(Problem inst, int challengeid){ this(inst, Option.none(), challengeid); } /** Provide a SECRET challenge*/ public ProvideTrans(Problem inst, Solution solution, int challengeid){ this(inst, Option.some(solution), challengeid); } public ProvideTrans install(int playerId, GameI game, boolean isOverTime){ return game.installTransaction(playerId, this); } public boolean isProvide(){ return true; } }} SolveTrans{{ public SolveTrans install(int playerId, GameI game, boolean isOverTime){ return game.installTransaction(playerId, this); } public boolean isSolve(){ return true; } }} ReofferTrans{{ public ReofferTrans install(int playerId, GameI game, boolean isOverTime){ if(!isOverTime){ return game.installTransaction(playerId, this); } return this; } public boolean isReoffer(){ return true; } }} PlayerTrans{{ public PlayerTrans applyTransactions(final GameI game, final boolean isOverTime){ final int playerID = getId().getId(); // Install/Update all the transactions return new PlayerTrans(getId(), getTs().map(new List.Map(){ public Transaction map(Transaction t){ return t.install(playerID, game, isOverTime); } })); } public List getOfferTrans(){ return (List)ts.filter(new List.Pred(){ public boolean huh(Transaction t){ return (t instanceof OfferTrans); } }); } public List getAcceptTrans(){ return (List)ts.filter(new List.Pred(){ public boolean huh(Transaction t){ return (t instanceof AcceptTrans); } }); } public List getProvidedTrans(){ return (List)ts.filter(new List.Pred(){ public boolean huh(Transaction t){ return (t instanceof ProvideTrans); } }); } public List getSolvedTrans(){ return (List)ts.filter(new List.Pred(){ public boolean huh(Transaction t){ return (t instanceof SolveTrans); } }); } public List getReofferTrans(){ return (List)ts.filter(new List.Pred(){ public boolean huh(Transaction t){ return (t instanceof ReofferTrans); } }); } }} PlayerContext{{ /** Is this context in an overtime Round? */ public boolean isOverTime(){ return config.isOverTime(currentRound); } /** Return all the Offered Challenges */ public List getAllOffered(){ return ourOffered.append(theirOffered); } /** * Is the give transaction legal for this PlayerContext... i.e., does it * break any of the game rules? */ public void isLegal(PlayerTrans trans){ AgentsAreActiveButNotHyperActive(trans); // Rule 2 AgentsProposeEnoughSecretChallenges(trans); // DontOfferOrAcceptIfOverTime(trans); offerNewChallenges(trans); // Rule 3 provideAllAccepted(trans);// Rule 4 offerValidProblemTypes(trans); provideCorrectProblems(trans);// Rule 4 solveFromProvided(trans);// Rule 4 checkReoffersDecrement(trans);// Rule 5 acceptFromOthers(trans);// Rule 6* dontAcceptAndReoffer(trans);// ** New Rule reofferFromOthers(trans);// Rule 7 currentPlayerResponded(trans); offerPricesInRange(trans); secretChallengesHaveSecrets(trans); } private void AgentsProposeEnoughSecretChallenges(PlayerTrans trans){ int numOffers = trans.getOfferTrans().length(); int secretOffers = 0; for (OfferTrans ot : trans.getOfferTrans()) { if (ot.kind.isSecret()) { secretOffers++; } } int requiredSecretOffers = (int) (config.getSecretRatio() * numOffers + (1.0 - config.getSecretRatio())); if (config.getHasSecrets() && secretOffers < requiredSecretOffers) { throw new GameI.BadTransException("Agent didn't offer enough secret challenges"); } if (!config.getHasSecrets() && secretOffers > 0) { throw new GameI.BadTransException("Agent should not offer any secret challenges"); } } private void AgentsAreActiveButNotHyperActive(PlayerTrans trans){ if (!isOverTime()) { int numProposals = trans.getOfferTrans().length() + trans.getReofferTrans().length(); int numOppositions = trans.getAcceptTrans().length() + trans.getReofferTrans().length(); int maxProposals = config.getMaxProposals(); int minProposals = config.getMinProposals(); int minOppositions = config.getMinPropositions(); if (numProposals < minProposals) { throw new GameI.BadTransException("Agent proposed " + numProposals + " but should have proposed at least " + minProposals); } if (numProposals > maxProposals) { throw new GameI.BadTransException("Agent proposed " + numProposals + " but should have proposed at most " + maxProposals); } // Player is not penalized if there aren't enough offered challenge. if (getTheirOffered().length() < minOppositions) { minOppositions = getTheirOffered().length(); } if (numOppositions < minOppositions) { throw new GameI.BadTransException("Agent opposed " + numOppositions + " but should have opposed at least " + minOppositions); } } } // private void DontOfferOrAcceptIfOverTime(PlayerTrans trans){ // // if (config.isOverTime(currentRound)) { // if (trans.getOfferTrans().length() > 0) { // throw new GameI.BadTransException("Offered challenges during overtime // rounds"); // } // if (trans.getAcceptTrans().length() > 0) { // throw new GameI.BadTransException("accepted challenges during overtime // rounds"); // } // } // } private void reofferFromOthers(PlayerTrans trans){ boolean allOffered = getTheirOffered().containsAllG(trans.getReofferTrans(), Config. getComp()); if (!allOffered) { throw new GameI.BadTransException("Reoffered a challenge not offered by others"); } } private void dontAcceptAndReoffer(PlayerTrans trans){ boolean overlap = trans.getAcceptTrans().containsAnyG(trans.getReofferTrans(), new List.GComp() { @Override public boolean comp(AcceptTrans a, ReofferTrans r){ return a.getChallengeid() == r.getChallengeid(); } }); if (overlap) { throw new GameI.BadTransException("Reoffered an Accepted Challenge"); } } private void checkReoffersDecrement(PlayerTrans trans){ for (ReofferTrans rt : trans.getReofferTrans()) { if(!hasChallenge(getTheirOffered(), rt.getChallengeid())) throw new GameI.BadTransException("Reoffered unknown challenge, ID: "+rt.getChallengeid()); double offeredPrice = findChallenge(getTheirOffered(), rt.getChallengeid()).getPrice(); if (rt.getPrice() >= offeredPrice || rt.getPrice() > 0 && Util.lessThan(offeredPrice - rt.getPrice(), config.getMindecr())) { throw new GameI.BadTransException("Reoffer by less than mindec (" + (offeredPrice - rt.getPrice()) + ")"); } } } private void solveFromProvided(PlayerTrans trans){ boolean allSolved = getProvided().sameG(trans.getSolvedTrans(), Config. getComp()); if (!allSolved) { throw new GameI.BadTransException("Solved and provided challenges don't match"); } } private void provideAllAccepted(PlayerTrans trans){ boolean same = getAccepted() .sameG(trans.getProvidedTrans(), Config. getComp()); if (!same) { throw new GameI.BadTransException("Didn't provide all problems"); } } private void secretChallengesHaveSecrets(PlayerTrans trans){ for (ProvideTrans proTrans : trans.getProvidedTrans()) { int challengeId = proTrans.getChallengeid(); for (AcceptedChallenge challenge : getAccepted()) { if (challenge.getKey() == challengeId) { if (challenge.getKind().isSecret()) { // secret challenge if (!proTrans.getSecret().isSome()) { throw new GameI.BadTransException("Secret not provided for secret challenge: " + challengeId); } } else { // All challenge if (proTrans.getSecret().isSome()) { throw new GameI.BadTransException("Secret provided for all challenge: " + challengeId); } } } } } } private void acceptFromOthers(PlayerTrans trans){ boolean allOffered = getTheirOffered().containsAllG(trans.getAcceptTrans(), Config. getComp()); if (!allOffered) { throw new GameI.BadTransException("Accepted a challenge not offered by others"); } } /** Offer challenges not in store */ private void offerNewChallenges(PlayerTrans trans){ boolean notFresh = getAllOffered().containsAnyG(trans.getOfferTrans(), new List.GComp() { public boolean comp(OfferedChallenge ch, OfferTrans trans){ return ch.getPred().equals(trans.getPred()); } }); if (notFresh) { throw new GameI.BadTransException("Challenge already in store"); } boolean duplicates = trans.getOfferTrans().containsAny(trans.getOfferTrans(), new List.Comp() { public boolean comp(OfferTrans t1, OfferTrans t2){ return (t1 != t2) && t1.getPred().equals(t2.getPred()); } }); if (duplicates) { throw new GameI.BadTransException("Offered Duplicate Challenges"); } } /** Offer prices in [0..1] */ private void offerPricesInRange(PlayerTrans trans){ if (trans.getOfferTrans().contains(new List.Pred() { @Override public boolean huh(OfferTrans t){ return t.getPrice() < 0 || 1.0 < t.getPrice(); } })) { throw new GameI.BadTransException("Offered Price Not in Range"); } } /** Offer valid problem types */ private void offerValidProblemTypes(PlayerTrans trans){ for (OfferTrans t : trans.getOfferTrans()) { Option error = config.getPredicate().valid(t.getPred()); if (error.isSome()) { throw new GameI.BadTransException("Illegal Type Offered:" + error.inner() + ". Given: " + t.getPred()); } } } /** Provide valid problems*/ private void provideCorrectProblems(PlayerTrans trans){ for (ProvideTrans proTrans : trans.getProvidedTrans()) { Problem problem = proTrans.getInst(); int challengeId = proTrans.getChallengeid(); for (AcceptedChallenge challenge : getAccepted()) { if (challenge.getKey() == challengeId) { Option res = config.getPredicate().valid(problem, challenge.getPred()); if (res.isSome()) { throw new GameI.BadTransException("Invalid Problem: " + res.inner()); } } } } } /** Check that the correct player has responded */ private void currentPlayerResponded(PlayerTrans trans){ if (!getId().equals(trans.getId())) { throw new GameI.BadTransException("Incorrect Player Responded"); } } /** Is the Challenge ID in the given List */ static boolean hasChallenge(List lst, final int chID){ return lst.contains(new List.Pred() { @Override public boolean huh(CH ch){ return ch.getKey() == chID; } }); } /** Find the given Challenge ID in the given List */ static CH findChallenge(List lst, final int chID){ return lst.find(new List.Pred() { @Override public boolean huh(CH ch){ return ch.getKey() == chID; } }); } }} /** * * Holds the game configuration parameters * Acts as a factory for player and challenge ids * */ Config{{ private final int START_PLAYER_ID = 100; private final int START_CHALLENGE_ID = 500; private int currentChallengeID = START_CHALLENGE_ID; private int currentPlayerID = START_PLAYER_ID; public int createPlayerID(){ return currentPlayerID++; } public int createChallengeID(){ return currentChallengeID++; } public boolean isOverTime(int round){ return round > getNumrounds(); } /** Get a general comparator that can compare Challenges to Transactions */ static edu.neu.ccs.demeterf.lib.List.GComp getComp(){ return new edu.neu.ccs.demeterf.lib.List.GComp() { @Override public boolean comp(CH ch, TR t){ return t.getChallengeid() == ch.getKey(); } }; } }} GameI{{ /** Handle an offer challenge transaction */ void installTransaction(int challengerID, OfferTrans ot); /** Handle an accept challenge transaction */ void installTransaction(int challengeeID, AcceptTrans at); /** Handle Provide Transactions */ void installTransaction(int challengerID, ProvideTrans pt); /** Handle Solve Transactions */ void installTransaction(int challengeeID, SolveTrans st); /** Handle Reoffer Transactions */ void installTransaction(int newChallenger, ReofferTrans rt); /** TransferMoney Between Accounts */ void transferMoney(PlayerID fromPlayerId, PlayerID toPlayerId, double amount); }} AccountTransaction{{ public abstract void applyTransaction(GameI game); }} AccountAcceptTrans{{ public void applyTransaction(GameI game){ game.transferMoney(acceptor, offerer, amount); } }} AccountSolveTrans{{ public void applyTransaction(GameI game){ game.transferMoney(offerer, acceptor, amount); } }} AccountRefundTrans{{ public void applyTransaction(GameI game){ game.transferMoney(offerer, acceptor, amount); } }}