// 最も近いユニットナンバー public int mostNear(int id1, int i, int id2) { int nearUnit = 0; int nearLength = 10; int length = 0; for (int j = 0; j < 3; j++) { length = GameBoard.distance(this.board.unitLocation[id1][i], this.board.unitLocation[id2][j]); if (length < nearLength) { nearUnit = j; nearLength = length; } } return nearUnit; }
/** * ユニットを任意の場所に動かす * * <p>2015/08/31追加 失敗する条件は次の通り ・現在の座標と移動先の座標が同じ場合 ・座標が範囲外を参照した場合 ・移動距離が1マス以上を超えた場合 * * @param x 動かした先の座標 * @param y 動かした先の座標 * @param index 動かすユニット番号 0-3 * @return trure 成功 false 失敗 */ public boolean movePos(int x, int y, int index) { int id = this.whoIsPlay(); // 範囲外参照チェック if (!GameBoard.availableArea(x, y)) { return false; } // 距離が0もしくは2以上の移動は無効 if (GameBoard.distance(unitLocation[id][index], new Point(x, y)) != 1) { return false; } unitLocation[id][index].x = x; unitLocation[id][index].y = y; this.lastIndex = index; this.lastX = x; this.lastY = y; this.lastId = this.whoIsPlay(); nextPhase(); return true; }
@Override public double STATIC_VALUE() { double score = 0.0; int enemyTeamId = playerTeamId == 0 ? 1 : 0; // 点数さによる評価 // バイアスをかけておかないとマイナスになる可能性がある score += k1 * (board.teamPoint[playerTeamId] - board.teamPoint[enemyTeamId] + 100); // 自分のユニットとタワーまでの距離 // 2015/09/03変更 // 平均値が小さく、分散が小さいものを選択する int eDist = 0; int pDist = 0; for (int i = 0; i < 4; i++) { pDist += Math.pow( GameBoard.distanceTower(board.unitLocation[playerTeamId][i]), 2); // 距離が多くなればなるほど点数が下がる eDist += Math.pow(GameBoard.distanceTower(board.unitLocation[enemyTeamId][i]), 2); // } score += k2 * (-pDist + eDist + 100); // 保持状態 int tower = 0; for (int i = 0; i < 3; i++) { if (board.towerHold[i] == playerTeamId) { tower++; } else { tower--; } } score += k3 * (tower + 3); // 相性評価 int length = 0; for (int i = 0; i < 3; i++) { // 全ての駒に対して int MNE = mostNear(playerTeamId, i, enemyTeamId); // MNE = mostNearEnemy Point P = this.board.unitLocation[playerTeamId][i]; Point E = this.board.unitLocation[enemyTeamId][MNE]; if (multiple(i, playerTeamId) < multiple(MNE, enemyTeamId)) // 相手のほうが多い { length -= GameBoard.distance(P, E); } else if (multiple(MNE, enemyTeamId) < multiple(i, playerTeamId)) // 自分のほうが多い { length += multiple(MNE, enemyTeamId); } else { // 同数 if ((i != 3 && i == MNE + 1) || (i == 3 && MNE == 0)) // 勝てる時 { length += multiple(MNE, enemyTeamId); } else if ((i != 0 && i == MNE - 1) || (i == 0 && MNE == 3)) // 負ける時 { length -= GameBoard.distance(P, E); } // 引き分けは加算無し } } score += k4 * length; // 最後に打った手が自分の手ならマイナスを付けておく // if(board.lastId==playerTeamId) score = -score; // 現在の局面が相手なら /* if (playerTeamId == board.whoIsPlay()) { score = -score; } */ this.score = score; return score; }
@Override public boolean isPlayerTurn() { return board.whoIsPlay() == this.playerTeamId; }
/** * ランダムにゲーム終了までプレイする * * @return ture プレイヤーの勝ち */ public boolean doPlayout() { // ゲームボードをコピーしおておく GameBoard playout = new GameBoard(this); // プレイアウト専用移動履歴 LinkedList<Hand> history = new LinkedList<>(); // ゲーム終了まで繰り返す while (playout.isGameEnd() == -1) { // 移動候補手 ArrayList<Hand> canditate = new ArrayList(); int id = playout.whoIsPlay(); for (int j = 0; j < 4; j++) { // すべての手について // タワーマスにいて苦手とするユニットが接近していなければタワーにいる boolean f4 = formula4(unitLocation, j, id, this.turnState, this.firstTeamId); if (!f4) continue; for (int i = 0; i < 8; i++) { int x = playout.unitLocation[id][j].x + movex[i]; int y = playout.unitLocation[id][j].y + movey[i]; // x,yが範囲外なら無視 if (!availableArea(x, y)) { continue; } // 定石1を適用 boolean f1 = formula1(movex[i], movey[i], x, y, id); if (!f1) continue; // 定石外 // 定石2を適用 boolean f2 = formula2(history, x, y, j, id); if (!f2) continue; // 定石外 // 定石3 /* boolean f3 = formula3(playout.unitLocation,x,y,j,id,playout.firstTeamId,playout.turnState); if(!f3) continue; */ // ここまできたら候補に追加 canditate.add(new Hand(x, y, j, id)); } } // 候補からランダム選択 // 候補が1個もない場合はgetAnyHand()でなんでもいいから動かす Hand hand; if (canditate.size() == 0) { hand = getAnyHand(this.whoIsPlay()); } else { int rand = (int) (canditate.size() * Math.random()); hand = canditate.get(rand); } // 動かす boolean result = playout.movePos(hand.x, hand.y, hand.index); // ログに保存 history.addLast(hand); if (history.size() > 5) history.removeFirst(); } // 勝ちの場合は勝ちの回数を増やす // ここを修正 // チームIDではなく現在プレイ中のプレイヤーID if (playout.isGameEnd() == this.whoIsPlay()) { return true; } return false; }
/** * 定石を適用し、考えられるすべての手を実行した子ノードのゲームボードを返します * * @return */ public ArrayList<GameBoard> extpand() { /** 子ノードを定石を使って拡張する */ ArrayList<GameBoard> children = new ArrayList<>(); // くじ引き ArrayList<Integer> omikuji = new ArrayList<Integer>(); omikuji.add(0); omikuji.add(1); omikuji.add(2); omikuji.add(3); int id = this.whoIsPlay(); // タワーに存在するユニットの数を取得 int towerUnit = 0; for (int j = 0; j < 4; j++) { if (isTowerPos(unitLocation[id][j])) towerUnit++; } // 実行可能手をすべて展開 for (int j = 0; j < 4; j++) { int index = omikuji.remove((int) (omikuji.size() * Math.random())); // すべてタワーにいない場合に限り // タワーマスにいて苦手とするユニットが接近していなければタワーにいる if (towerUnit < 3) { boolean f4 = formula4(unitLocation, index, id, this.turnState, this.firstTeamId); if (!f4) continue; } for (int i = 0; i < 8; i++) { int x = this.unitLocation[id][index].x + movex[i]; int y = this.unitLocation[id][index].y + movey[i]; // x,yが範囲外なら無視 if (!availableArea(x, y)) { continue; } boolean f1 = formula1(movex[i], movey[i], x, y, id); // 移動範囲漏れチェック if (!f1) continue; // 定石外 // boolean f2 = formula2(handHistory,x,y,index,id); // 過去の動き重複チェック // if(!f2) continue; // 定石外 // ボードを複製し、動かす GameBoard tmp = new GameBoard(this); tmp.move(movex[i], movey[i], index); tmp.handHistory.addLast(new Hand(x, y, index, id)); // 一定数以上の履歴は破棄する if (tmp.handHistory.size() > HAND_HISTORY_NUM) { tmp.handHistory.removeFirst(); } // 候補に追加 children.add(tmp); } } return children; }