Package com.jcloisterzone.ai

Source Code of com.jcloisterzone.ai.SelectActionTask

package com.jcloisterzone.ai;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.eventbus.Subscribe;
import com.jcloisterzone.Player;
import com.jcloisterzone.action.MeepleAction;
import com.jcloisterzone.action.PlayerAction;
import com.jcloisterzone.action.SelectFeatureAction;
import com.jcloisterzone.action.SelectFollowerAction;
import com.jcloisterzone.action.SelectTileAction;
import com.jcloisterzone.action.TilePlacementAction;
import com.jcloisterzone.ai.choice.ActionChoice;
import com.jcloisterzone.ai.choice.AiChoice;
import com.jcloisterzone.ai.choice.PassChoice;
import com.jcloisterzone.ai.choice.TilePlacementChoice;
import com.jcloisterzone.board.Position;
import com.jcloisterzone.board.TilePlacement;
import com.jcloisterzone.board.TileSymmetry;
import com.jcloisterzone.board.pointer.FeaturePointer;
import com.jcloisterzone.board.pointer.MeeplePointer;
import com.jcloisterzone.event.SelectActionEvent;
import com.jcloisterzone.figure.Phantom;
import com.jcloisterzone.figure.SmallFollower;
import com.jcloisterzone.game.Game;
import com.jcloisterzone.game.phase.ActionPhase;
import com.jcloisterzone.game.phase.EscapePhase;
import com.jcloisterzone.game.phase.PhantomPhase;
import com.jcloisterzone.game.phase.Phase;
import com.jcloisterzone.game.phase.TowerCapturePhase;
import com.jcloisterzone.game.phase.WagonPhase;

public class SelectActionTask implements Runnable {

    protected final transient Logger logger = LoggerFactory.getLogger(getClass());

    private final RankingAiPlayer aiPlayer;
    private final SelectActionEvent rootEv;
    private final Deque<AiChoice> queue = new LinkedList<>();

    private AiChoice choice = null;
    private AiChoice bestSoFar = null;
    private double bestSoFarRanking = Double.NEGATIVE_INFINITY;

    private SavePointManager spm;
    private Game game;

    @SuppressWarnings("unchecked")
    private static final List<Class<? extends Phase>> ALLOWED_IN_PHASE_LOOP = Lists.newArrayList(ActionPhase.class, EscapePhase.class, TowerCapturePhase.class, WagonPhase.class);

    public SelectActionTask(RankingAiPlayer aiPlayer, SelectActionEvent rootEv) {
        this.aiPlayer = aiPlayer;
        this.rootEv = rootEv;
    }

    private void dbgPringHeader() {
        StringBuilder sb = new StringBuilder("*** ranking start * ");
        sb.append(game.getPhase().getClass().getSimpleName());
        sb.append(" * ");
        Player p = game.getActivePlayer();
        sb.append(p.getNick());
        sb.append(" ***");
        System.out.println(sb);
    }

    private void dbgPringFooter() {
        System.out.println("=== selected chain (reversed) " + bestSoFarRanking);
        AiChoice step = bestSoFar;
        while (step != null) {
            System.out.print("  - ");
            System.out.println(step.toString());
            step = step.getPrevious();
        }
        System.out.println("*** ranking end ***");
    }

    private void dbgPringStep(AiChoice choice, boolean isFinal) {
        AiChoice ac = choice;
        StringBuilder sb = new StringBuilder("  ");
        while (ac.getPrevious() != null) {
            sb.append("  ");
            ac = ac.getPrevious();
        }
        sb.append("- ").append(choice.toString()).append(" ");

        while (sb.length() < 80) {
           sb.append(".");
        }
        sb.append(" ").append(String.format(Locale.ROOT, "%10.5f", choice.getRanking()));
        if (isFinal) {
            sb.append(" ...... ");
            sb.append(String.format(Locale.ROOT, "%10.5f", choice.getChainRanking()));
        }
        System.out.println(sb);
    }


    @Override
    public void run() {
        //logger.info("Select action task started " + aiPlayer.getClientStub().getGame().getTilePack().size() + " " + rootEv.getPlayer() + " > " + rootEv.getActions().toString());
        boolean dbgPrint = false;
        try {
            this.game = aiPlayer.copyGame(this);
            if (dbgPrint) dbgPringHeader();

            spm = new SavePointManager(game);
            spm.startRecording();

            handleActionEvent(rootEv);

            while (!queue.isEmpty()) {
                choice = queue.pop();
                spm.restore(choice.getSavePoint());
                choice.perform(game.getPhase());
                boolean isFinal = phaseLoop();
                choice.rankPartial(aiPlayer.getGameRanking(), game);
                if (isFinal) {
                    rankFinal(choice);
                }
                if (dbgPrint) dbgPringStep(choice, isFinal);
            }
            if (dbgPrint) dbgPringFooter();
            //logger.info("Select action task finished "  + game.getTilePack().size() + " " + rootEv.getPlayer() + " > " + rootEv.getActions().toString() + " " + bestSoFar.chainToString());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        if (bestSoFar == null) {
            //in perfect world it should never happen
            aiPlayer.setBestChain(null);
            aiPlayer.selectDummyAction(rootEv.getActions(), rootEv.isPassAllowed());
        } else {
            aiPlayer.setBestChain(bestSoFar);
            aiPlayer.popActionChain();
        }
    }

    private boolean phaseLoop() {
        Phase phase = game.getPhase();
        while (!phase.isEntered()) {
            if (!Iterables.contains(ALLOWED_IN_PHASE_LOOP, phase.getClass())) {
                return true;
            }
            phase.setEntered(true);
            phase.enter();
            phase = game.getPhase();
            game.flushEventQueue();
        }
        game.flushEventQueue();
        return false;
    }

    private void rankFinal(AiChoice step) {
        step.setRanking(step.getRanking() + aiPlayer.getGameRanking().getFinal(game));
        double currChainRanking = step.getChainRanking();
        if (currChainRanking > bestSoFarRanking) {
            bestSoFar = step;
            bestSoFarRanking = currChainRanking;
        }
    }

    /*
     * possible improvements (TODO)
     *  - pass action for Abbey don't count there is another tile which brings some points
     *  - Wagon usually not moved (worse rank then pass, expect cloister), common ranking action don't know anything about following wagon phase1
     *    and wagon is ranked separately
     */

    @Subscribe
    public void handleActionEvent(SelectActionEvent ev) {
        if (!game.getActivePlayer().equals(aiPlayer.getPlayer())) {
            return; //e.g. wagon move of other player
        }
        //logger.info("....T " + game.getTilePack().size() + "|" + ev.getPlayer() + " > " + ev.getActions().toString());
        List<MeepleAction> meepleActions = new ArrayList<MeepleAction>();
        SavePoint savePoint = spm.save();

        for (PlayerAction<?> action : ev.getActions()) {
            if (action instanceof MeepleAction) {
                meepleActions.add((MeepleAction) action);
            } else if (action instanceof TilePlacementAction) {
                handleTilePlacementAction(savePoint, (TilePlacementAction) action);
            } else if (action instanceof SelectTileAction) {
                handleSelectTileAction(savePoint, (SelectTileAction) action);
            } else if (action instanceof SelectFeatureAction) {
                handleSelectFeatureAction(savePoint, (SelectFeatureAction) action);
            } else if (action instanceof SelectFollowerAction) {
                handleSelectFollowerAction(savePoint, (SelectFollowerAction) action);
            }
        }

        if (!meepleActions.isEmpty()) {
            for (MeepleAction action : preprocessMeepleActions(meepleActions)) {
                handleSelectFeatureAction(savePoint, action);
            }
        }

        if (ev.isPassAllowed()) {
            queue.push(new PassChoice(choice, savePoint));
        }
    }

    protected void handleTilePlacementAction(SavePoint savePoint, TilePlacementAction action) {
        TileSymmetry sym = action.getTile().getSymmetry();
        //do not symmetric tiles
        if (sym == TileSymmetry.S4) {
            List<TilePlacement> options = new ArrayList<>(action.getOptions());
            Collections.sort(options);
            int size = options.size();
            for (int i = 0; i < size; i++) {
                TilePlacement tp = options.get(i);
                if (i == 0 || !options.get(i-1).getPosition().equals(tp.getPosition())) {
                    queue.push(new TilePlacementChoice(choice, savePoint, action, tp));
                }
            }
        } else {
            for (TilePlacement tp : action.getOptions()) {
                queue.push(new TilePlacementChoice(choice, savePoint, action, tp));
            }
        }
    }

    protected void handleSelectTileAction(SavePoint savePoint, SelectTileAction action) {
        for (Position pos : action.getOptions()) {
            queue.push(new ActionChoice<Position>(choice, savePoint, action, pos));
        }
    }

    protected void handleSelectFeatureAction(SavePoint savePoint, SelectFeatureAction action) {
        for (FeaturePointer fp : action.getOptions()) {
            queue.push(new ActionChoice<FeaturePointer>(choice, savePoint, action, fp));
        }
    }


    protected void handleSelectFollowerAction(SavePoint savePoint, SelectFollowerAction action) {
        for (MeeplePointer mp : action.getOptions()) {
            queue.push(new ActionChoice<MeeplePointer>(choice, savePoint, action, mp));
        }
    }

    //don't count again for phantom
    protected Collection<MeepleAction> preprocessMeepleActions(List<MeepleAction> actions) {
        if (game.getPhase() instanceof PhantomPhase) return Collections.emptyList();
        boolean hasSmallFollower = false;
        boolean hasPhantom = false;
        for (MeepleAction a : actions) {
            if (a instanceof MeepleAction && a.getMeepleType().equals(SmallFollower.class))
                hasSmallFollower = true;
            if (a instanceof MeepleAction && a.getMeepleType().equals(Phantom.class))
                hasPhantom = true;
        }
        if (hasSmallFollower && hasPhantom) {
            return Collections2.filter(actions, new Predicate<MeepleAction>() {
                @Override
                public boolean apply(MeepleAction a) {
                    return !(a.getMeepleType().equals(Phantom.class));
                }
            });
        }
        return actions;
    }

    // ---- refactor done boundary -----


//        protected void rankTowerPiecePlacementOnTile(final Tile currTile, final TowerPieceAction towerPieceAction, final Position towerPiecePos) {
//            this.interactionHandler = new AiInteractionAdapter() {
//                public void selectAction(List<PlayerAction> actions, boolean canPass) {
//                    phaseLoop();
//                    SavePoint sp = spm.save();
//                    TakePrisonerAction prisonerAction = (TakePrisonerAction) actions.get(0);
//                    for (Entry<Position, Set<Location>> entry : prisonerAction.getLocationsMap().entrySet()) {
//                        Position pos = entry.getKey();
//                        for (Location loc : entry.getValue()) {
//                            for (Meeple m : getBoard().get(pos).getFeature(loc).getMeeples()) {
//                                game.getPhase().takePrisoner(pos, loc, m.getClass(), m.getPlayer().getIndex());
//                                double currRank = rank();
//                                if (currRank > bestSoFar.getRank()) {
//                                    bestSoFar = new PositionRanking(currRank, currTile.getPosition(), currTile.getRotation());
//                                    Deque<SelectedAction> sa = bestSoFar.getSelectedActions();
//                                    sa.add(new SelectedAction(towerPieceAction, towerPiecePos, null));
//                                    sa.add(new SelectedAction(prisonerAction, pos, loc, m.getClass(), m.getPlayer()));
//                                }
//                                spm.restore(sp);
//                            }
//                        }
//                    }
//                };
//            };
//            game.getPhase().placeTowerPiece(towerPiecePos);
//        }

//        protected void rankTowerPiecePlacement(Tile currTile,TowerPieceAction action) {
//            AiInteraction interactionHandlerBackup = this.interactionHandler;
//            SavePoint sp = spm.save();
//            for (Position pos: action.getSites()) {
//                rankTowerPiecePlacementOnTile(currTile, action, pos);
//                spm.restore(sp);
//            }
//
//            this.interactionHandler = interactionHandlerBackup;
//        }

}
TOP

Related Classes of com.jcloisterzone.ai.SelectActionTask

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.