/**
 * $Id: A5BDA1BCA5B92852656E7361596F6D693429.txt,v 1.1.1.1 2009/06/01 12:21:03 kamoi Exp $
 * Copyright 2004 kamoland.com All rights reserved.
 */
 package com.kamoland.rensacommon.ai.decider;
 
 import com.kamoland.rensacommon.Const;
 import com.kamoland.rensacommon.ai.AiBasedCom;
 import com.kamoland.rensacommon.ai.AiUtil;
 import com.kamoland.rensacommon.ai.Tumi;
 import com.kamoland.rensacommon.rule.PuField;
 
 /**
 * 連鎖形成読みの実装 part4
 *
 * @author kamoland.com
 * @version
 * <pre>
 * 2004.06.19 新規作成
 * </pre>
 */
 public class RensaYomi4ABC implements AiBasedCom {
 
    /**
     * 動作モード
     */
    int mode;
    
    /**
     * デフォルトコンストラクタ
     */
    public RensaYomi4ABC() {
    }
 
    /**
     * パラメータ付きコンストラクタ
     * @param mode
     */
    public RensaYomi4ABC(int mode) {
        this.mode = mode;
    }
 
    /**
     * COMとしての表示名を取得する
     * @return COMとしての表示名
     */
    public String getDisplayName() {
        switch (mode) {
        default:
        return "RensaYomi4";
        }
    }
    
    /**
     * 説明コメントを取得する
     * @return 説明コメントを取得する
     */
    public String getComment() {
        switch (mode) {
        default:
        return "NEXT2まで読む.思考に時間がかかって,積みは遅い";
        }
    }
 
    /**
     * 積み込み時間間隔を求める
     * これが大きいほど積みが遅い
     * @return 積み込み時間間隔(msec)
     */
    public long getTumiInterval() {
        switch (mode) {
        default:
            return 45;
        }
    }
 
    /**
     * 部屋のルールを求める
     * @return
     */
    public int getRoomRule() {
        return 0;
    }
 
    /**
     * 積みに対する評価得点を算出する
     * @param beforeField 自分のフィールド(NEXT積み適用前)
     * @param afterField 自分のフィールド(NEXT積み適用後)
     * @param fallenTumi NEXT積み(重力落下後)
     * @param nextTumos ツモ色.NEXT:[0][0]〜[0][1],NEXT2:[1][0]〜[1][1]
     * @param enemyFields 敵のフィールド
     * @param isAnyEnemyDoingRensa いずれかの敵が連鎖を発動中
     */
    public long evaluateTumi(
        PuField beforeField,
        PuField afterField,
        Tumi fallenTumi,
        byte[][] nextTumos,
        PuField[] enemyFields,
        boolean isAnyEnemyDoingRensa) {
 
        int fpRange[] = new int[]{
            3, 20, 30000, Const.FIELD_Y * 2 + 1};
        int fp[] = new int[fpRange.length];
        
        boolean isRensaStart = afterField.duplicate().vanish(0).any_rensa;
        
        int rensaCount = 0;
        if (isRensaStart) {
            // もし連鎖が発動するなら,連鎖数をカウントする
            PuField target = afterField.duplicate();
            do {
                rensaCount++;
                target = target.createVanishedField();
                target.gravitate();
            }   while (target.vanish(rensaCount).any_rensa);
        }
        
        // ★ 窒息を予防する
        int df = 0; 
        for (int k = 0; k < 2; k++) {
            if (fallenTumi.getI()[k] == 2 && fallenTumi.getJ()[k] <= 1 && !isRensaStart) {
                // 窒息する
            } else {
                // 窒息しない
                df++;
            }
        }
        fp[0] = df; // [0,2]
        
        // ★ 連鎖の誤発動を予防する.また,大きい連鎖の発動を促進する
        // 発火許可状態:この積みで発動する連鎖数(1〜19)
        // 発火不可状態:もしこの積みで連鎖が発動するなら0.発動しないなら1
        if (isFireEnabled(beforeField)) {
 
            // 中央部が詰まってきたら,発火しても良い
                fp[1] = rensaCount; // [1,19]
            
        } else {
            if (!isAnyEnemyDoingRensa) {
                // 余裕があり,かつ敵が連鎖中でないなら,発火不可
                fp[1] = isRensaStart? 0: 1;
            }
        }
 
        // NEXTのみとNEXT2も使って読んだ場合で,大きい方を採用する
        int ry1 = evaluateByRensaYomi(afterField, fallenTumi, 2, false);
        int ry2 = evaluateNextTumoByRensaYomi(afterField, nextTumos[1]);
        if (ry1 > ry2) {
            fp[2] = ry1;
        } else {
            fp[2] = ry2;
        }
 
        // ★ できるだけ下に置く
        fp[3] = fallenTumi.getJ()[0] + fallenTumi.getJ()[1]; // [0,Const.FIELD_Y*2]
 
        // 各ルールによる評価点を,優先順位を考慮して集計する
        long result = 0;
        long eff = 1;       
        for (int i = fp.length - 1; i >= 0; i--) {
            result += eff * fp[i];
            eff *= fpRange[i];
        }
 
        if (false) {
            StringBuffer mes = new StringBuffer();
            for (int i = 0; i < fpRange.length; i++) {
                mes.append("fp[" + i + "]=" + fp[i] + ", ");
            }
            mes.append("res=" + result);
            System.out.println(mes.toString());
        }
 
        return result;
    }
 
    /**
     * 発火しても良い状態かを判断する
     * @param beforeField
     * @return
     */ 
    private boolean isFireEnabled(PuField beforeField) {
            return (beforeField.getPuyo(2, 3) > 0 ||
                beforeField.getPuyo(3, 3) > 0 ||
                beforeField.getPuyo(1, 0) > 0 ||
                beforeField.getPuyo(4, 3) > 0);
    }
    
    /**
     * 積みを,連鎖読みを使ったルールで評価する
     * @param afterField
     * @param fallenTumi
     * @param minCon
     * @param rensaOnly
     * @return 評価結果
     */
    private int evaluateByRensaYomi(PuField afterField, Tumi fallenTumi, int minCon, boolean rensaOnly) {
 
        int cc = 0;
        if (!rensaOnly) {
            // ★ 連結個数を求める
            // 発火候補点で発火された状況も考慮する版
            int cc1 = AiUtil.countConnected(afterField, fallenTumi.getI()[0], fallenTumi.getJ()[0], fallenTumi.getCol()[0]);
            int cc2 = AiUtil.countConnected(afterField, fallenTumi.getI()[1], fallenTumi.getJ()[1], fallenTumi.getCol()[1]);
            if (cc1 > 4) {
                // 長連規制
                cc1 = 4;
            }
            if (cc2 > 4) {
                // 長連規制
                cc2 = 4;
            }
            cc = cc1 + cc2; // [0,32] * 2 = [0,64]
        }
    
        // 発火候補点で発火した結果
        PuField[] fired = AiUtil.imagineFiredField(afterField, minCon);
        int maxRensa = 0;
        for (int i = 0; i < fired.length; i++) {                        
            int currentRensa = 0;
            PuField target = fired[i];
            while (target.vanish(currentRensa).any_rensa) {
                currentRensa++;
                target = target.createVanishedField();
                target.gravitate();
            }
            if (currentRensa > maxRensa) {
                maxRensa = currentRensa;
            }
        }
        return cc + maxRensa * maxRensa; // [0,2^19]
    }
 
    /**
     * 所定のフィールド+ツモで連鎖読みを使ったルールを使い,評価したときの最善手の得点を求める
     * つまり,所定のフィールド+ツモでは,どれだけ良い手が見つかるのかを評価する
     * @param field
     * @param nextTumo
     * @return
     */
    public int evaluateNextTumoByRensaYomi(PuField field, byte[] nextTumo) {
 
        PuField fb = field.duplicate();
 
        Tumi beforeDrop = new Tumi();
        int[] points = new int[Const.FIELD_X * 2 + (Const.FIELD_X - 1) * 2];
        int firstTumiIndex = 0;
        int k = 0;
        final int[][] fallX = {{2, 1, 0}, {3, 4, 5}};
        final int minCon = 2;
        
        // 縦置き
        int d = 0;
        int i = 0;
        while (d < 2) {
            int x = fallX[d][i];
            
            PuField fa = fb.duplicate();
 
            if (fa.getPuyo(x, 0) == 0 && fa.getPuyo(x, 1) == 0) {
                // 最上段が埋まっていないので落とせる列
                beforeDrop.setElement(0, x, 0, nextTumo[0]);
                beforeDrop.setElement(1, x, 1, nextTumo[1]);
                Tumi dropped = AiUtil.falldown(fa, beforeDrop);
                fa.putPuyo(x, dropped.getJ()[0], dropped.getCol()[0]);
                fa.putPuyo(x, dropped.getJ()[1], dropped.getCol()[1]);
                
                // 評価得点と積み手を記録する
                points[k] = evaluateByRensaYomi(fa, dropped, minCon, true);
                firstTumiIndex = k;
                k++;
 
                // 上下入れ替え
                fa = fb.duplicate();
                beforeDrop.setElement(0, x, 0, nextTumo[1]);
                beforeDrop.setElement(1, x, 1, nextTumo[0]);
                dropped = AiUtil.falldown(fa, beforeDrop);
                fa.putPuyo(x, dropped.getJ()[0], dropped.getCol()[0]);
                fa.putPuyo(x, dropped.getJ()[1], dropped.getCol()[1]);
                
                // 評価得点と積み手を記録する
                points[k] = evaluateByRensaYomi(fa, dropped, minCon, true);
                k++;
 
                i++;
 
                if (i >= fallX[d].length) {
                    i = 0;
                    d++;
                }
                
            } else {
                // 最上段が埋まっているので方向を変える
                i = 0;
                d++;
            }
        }
        // 横置き
        d = 0;
        i = 0;
        while (d < 2) {
            int x = fallX[d][i];
 
            PuField fa = fb.duplicate();
 
            if (fa.getPuyo(x, 0) == 0 && fa.getPuyo(x + 1, 0) == 0) {
                beforeDrop.setElement(0, x, 0, nextTumo[0]);
                beforeDrop.setElement(1, x + 1, 0, nextTumo[1]);
                Tumi dropped = AiUtil.falldown(fa, beforeDrop);
                fa.putPuyo(x, dropped.getJ()[0], dropped.getCol()[0]);
                fa.putPuyo(x + 1, dropped.getJ()[1], dropped.getCol()[1]);
            
                // 評価得点と積み手を記録する
                points[k] = evaluateByRensaYomi(fa, dropped, minCon, true);
                firstTumiIndex = k;
                k++;                    
 
                // 左右入れ替え
                fa = fb.duplicate();
                beforeDrop.setElement(0, x, 0, nextTumo[1]);
                beforeDrop.setElement(1, x + 1, 0, nextTumo[0]);
                dropped = AiUtil.falldown(fa, beforeDrop);
                fa.putPuyo(x, dropped.getJ()[0], dropped.getCol()[0]);
                fa.putPuyo(x + 1, dropped.getJ()[1], dropped.getCol()[1]);
            
                // 評価得点と積み手を記録する
                points[k] = evaluateByRensaYomi(fa, dropped, minCon, true);
                k++;
                
                i++;
 
                if (i >= fallX[d].length || (d == 1 && i >= fallX[d].length - 1)) {
                    i = 0;
                    d++;
                }
                
            } else {
                // 最上段が埋まっているので方向を変える
                i = 0;
                d++;
            }
        }   
        
        // 積み手のうち,最高得点のものを求める     
        int maxPoint = points[firstTumiIndex];
        for (i = 1; i < k; i++) {
            if (points[i] > maxPoint) {
                maxPoint = points[i];
            }
        }
            
        return maxPoint;            
    }   
 }


© 2024 KMIソフトウェア