/** * $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; } } |