📄 smartagentbase.java
字号:
package com.sillysoft.lux.agent;import com.sillysoft.lux.*;import com.sillysoft.lux.util.*;//// SmartAgentBase.java// Lux//// Copyright (c) 2002-2007 Sillysoft Games. // http://sillysoft.net// lux@sillysoft.net//// This source code is licensed free for non-profit purposes. // For other uses please contact lux@sillysoft.net////// An abstract agent class containing a variety of utility methods for// subclasses to use//import java.util.Random;import java.util.List;import java.util.ArrayList;public abstract class SmartAgentBase implements LuxAgent {// This agent's ownerCode:protected int ID;// We store some refs to commonly used board objectsprotected Board board;protected Country[] countries;protected int numCountries;protected int numContinents;// Sometimes in the attack phase we know if we want to move in armies or not.// Store the value in this var. It will be used as long as it is not equal to -1.protected int moveInMemory;// Sometimes it is useful to remember which continent we are spending our efforts on.// When not in use set to -1.protected int goalCont;// Since all subclasses need a random number generator (for you-won strings) we keep one. protected Random rand;public SmartAgentBase() { rand = new Random(); goalCont = -1; moveInMemory = -1; }public void setPrefs( int newID, Board theboard ) { ID = newID; board = theboard; countries = board.getCountries(); numCountries = countries.length; numContinents = board.getNumberOfContinents(); }// SmartAgentBase leaves it up to subclasses to implement the following methods:public abstract String name();public abstract int pickCountry();public abstract void placeArmies( int numberOfArmies );public abstract void attackPhase();public abstract int moveArmiesIn( int cca, int ccd);public abstract void fortifyPhase();public abstract String youWon();// I have yet to develope any card-smarts. The game-world will automatically cash in our best set if we return from this method and still have five or more cards.public void cardsPhase( Card[] cards ) { mustKillPlayer = -1; // just in case it was set last turn. // xxagentxx SmartAgentBase cards phase }public void cashCardsIfPossible(Card[] cards) { if (Card.containsASet(cards)) { Card[] set = Card.getBestSet( cards, ID, countries ); board.cashCards(set[0], set[1], set[2]); } }// This method sets the class-variable goalCont to the continent with the least number of borders.// First all totally non-occupied conts will be searched, then partially ocupied ones.// Conts worth 0 or less are not considered.protected void setGoalToLeastBordersCont() { goalCont = -1; int[] borderSizes = new int[numContinents]; int smallBorders = 1000000; // the size of the smallest borders cont // first loop through and find the smallest totally empty cont for (int i = 0; i < numContinents; i++) { if (board.getContinentBonus(i) > 0) { borderSizes[i] = BoardHelper.getContinentSize(i, countries); if (borderSizes[i] < smallBorders && BoardHelper.playerOwnsContinent(-1, i, countries)) { smallBorders = borderSizes[i]; goalCont = i; } } } // if there were no empty conts then next we would like the cont with the least # of borders that is partially empty if (goalCont == -1) { smallBorders = 1000000; for (int i = 0; i < numContinents; i++) { if (board.getContinentBonus(i) > 0) { if (borderSizes[i] < smallBorders && BoardHelper.playerOwnsContinentCountry(-1, i, countries)) { smallBorders = borderSizes[i]; goalCont = i; } } } } // There is the possibility that no cont will be chosen, if all the continents with // open countries are worth 0 or less income }// If goalCont is set then return a country-code of the country we should choose in that cont. If goalCont is unset then set it to the smallest empty/open cont.protected int pickCountryInSmallContinent() { if (goalCont == -1 || ! BoardHelper.playerOwnsContinentCountry(-1, goalCont, countries)) // then we don't have a target cont yet { goalCont = -1; goalCont = BoardHelper.getSmallestPositiveEmptyCont(countries, board); if (goalCont == -1) // oops, there are no unowned conts goalCont = BoardHelper.getSmallestPositiveOpenCont(countries, board); } // if we are here then we DO have a target cont. return pickCountryInContinent( goalCont ); }// return an unowned country-code in <continent>, preferably near others we own// If there are no countries left in the given continent then pick a country touching us.protected int pickCountryInContinent(int continent) { CountryIterator continentIter = new ContinentIterator(continent, countries); while (continentIter.hasNext()) { Country c = continentIter.next(); if (c.getOwner() == -1 && c.getNumberPlayerNeighbors(ID) > 0) return c.getCode(); } // we neighbor none of them, so pick the open country with the fewest neighbors continentIter = new ContinentIterator(continent, countries); int bestCode = -1; int fewestNeib = 1000000; while (continentIter.hasNext()) { Country c = continentIter.next(); if (c.getOwner() == -1 && c.getNumberNeighbors() < fewestNeib) { bestCode = c.getCode(); fewestNeib = c.getNumberNeighbors(); } } if (bestCode == -1) { // There are no unowned countries in this continent. return pickCountryTouchingUs(); } return bestCode; }/** Pick the open country that touches us the most. */protected int pickCountryTouchingUs() { int maxTouches = -1; int maxCode = -1; // the country code of the best place so far // Loop through all the unowned countries CountryIterator ci = new PlayerIterator(-1, countries); while (ci.hasNext()) { Country open = ci.next(); if (open.getNumberPlayerNeighbors(ID) > maxTouches && board.getContinentBonus(open.getContinent()) >= 0) { maxTouches = open.getNumberPlayerNeighbors(ID); maxCode = open.getCode(); } } if (maxTouches < 1) { // Then no open countries touch any of our countries directly. // Do a search outwards to find the closest unowned country to us. List ourBorderCountries = new ArrayList(); ci = new PlayerIterator(ID, countries); while (ci.hasNext()) { Country open = ci.next(); if (open.getNumberNotPlayerNeighbors(ID) > 0) { ourBorderCountries.add(open); } } return BoardHelper.closestCountryWithOwner(ourBorderCountries, -1, countries); } return maxCode; }// returns the country-code of the nearest unowned country to the cluster starting at <root>// NOTE, this method is inferior to pickCountryTouchingUs().protected int pickCountryNearCluster( Country root ) { // do a breadth first search outwards starting with this cluster's borders // return as soon as we find an unowned country CountryIterator borders = new ClusterBorderIterator( root ); List cluster = new ArrayList(); while (borders.hasNext()) { cluster.add(borders.next()); } for (int i = 0; i < cluster.size(); i++) { CountryIterator neighbors = new NeighborIterator( (Country)cluster.get(i) ); while ( neighbors.hasNext()) { Country neighbor = neighbors.next(); if ( neighbor.getOwner() == -1 ) return neighbor.getCode(); else if( !cluster.contains(neighbor)) { // Then we add it to the List. in time its neighbors will get expanded cluster.add(neighbor); } } } // we should never get here. System.out.println("ERROR in smartbase.pickCountryNearCluster() 65465477"); return -1; }public void placeInitialArmies( int numberOfArmies ) { placeArmies( numberOfArmies ); }// a simplistic method to get the easiest continent for us to take overprotected int getEasiestContToTake() { // For each continent we calculate the ratio of (our armies):(enemy armies) // The biggest one wins. float easiestContRatio = -1; int easiestCont = -1; for (int cont = 0; cont < numContinents; cont++) { int enemies = BoardHelper.getEnemyArmiesInContinent( ID, cont, countries ); int ours = BoardHelper.getPlayerArmiesInContinent( ID, cont, countries ); float newratio = (float)ours/(float)enemies; if (newratio > easiestContRatio && board.getContinentBonus(cont) > 0) { easiestCont = cont; easiestContRatio = newratio; } } return easiestCont; }// Do we own all of the continents that this country borders?// NOTE: This will not check countries that are in the same continent as 'center'protected boolean weOwnContsArround(Country center) { int cont = center.getContinent(); CountryIterator n = new NeighborIterator(center); while (n.hasNext()) { Country neib = n.next(); if (neib.getContinent() != cont && ! BoardHelper.playerOwnsContinent(ID, neib.getContinent(), countries)) { return false; } } return true; }// should be called within placeArmies()// will place them at the start of the cheapest path to <wantCont>protected void placeArmiesToTakeCont( int numberOfArmies, int wantCont ) { if (wantCont == -1) { // we weren't given a continent index. maybe there is only 1 continent. fallback to another method placeArmiesOnClusterBorder( numberOfArmies, BoardHelper.getPlayersBiggestArmy(ID, countries) ); return; } // we want to place our armies strategically, in order to conquer <wantCont> if (BoardHelper.playerOwnsContinent( ID, wantCont, countries)) { // then we already own it, place on the weakest borders that we don't envelope int[] borders = BoardHelper.getContinentBorders(wantCont, countries); int placed = 0; while ( placed < numberOfArmies ) { int leastArmies = 1000000, leastID = -1; for (int i = 0; i < borders.length; i++) { if (countries[borders[i]].getArmies() < leastArmies && ! weOwnContsArround(countries[borders[i]])) { leastArmies = countries[borders[i]].getArmies(); leastID = borders[i]; } } if (leastID == -1) { // this can happen when the entire map is one continent. thus it has no borders leastID = borders[rand.nextInt(borders.length)]; } board.placeArmies(1, leastID); placed++; } return; } // if we own any countries in <wantCont>, then place the armies on the one with the most enemies inside the continent. CountryIterator want = new ContinentIterator(wantCont, countries); Country bestPlace = null; int mostEnemies = 0; while (want.hasNext()) { Country us = want.next(); if (us.getOwner() == ID) { // count its enemy neighbors inside wantCont: int enemyNeighbors = 0; CountryIterator neighbors = new NeighborIterator(us); while (neighbors.hasNext()) { Country neighbor = neighbors.next(); if (neighbor.getOwner() != ID && neighbor.getContinent() == wantCont) enemyNeighbors++; } if (enemyNeighbors > mostEnemies) { mostEnemies = enemyNeighbors; bestPlace = us; } } } // if we found anyplace at all, do it if (bestPlace != null) { board.placeArmies(numberOfArmies, bestPlace); return; } // If we got here then we own zero countries inside <wantCont> // we place our armies in the country we own with the cheapest route to <wantCont> int[] route = BoardHelper.cheapestRouteFromOwnerToCont( ID, wantCont, countries ); debug("BoardHelper.cheapestRouteFromOwnerToCont("+ID+", "+wantCont+") = "+new CountryRoute(route, countries)); int placer = route[0]; board.placeArmies(numberOfArmies, placer); }// this method places armies one at a time on the weakest border surrounding <root>// it will call itself untill all armies have been placed.protected void placeArmiesOnClusterBorder( int numberOfArmies, Country root ) { if (root==null) System.out.println("SmartBase.placeArmiesOnClusterBorder() -> the cluster root==null. 654213465"); CountryIterator borders = new ClusterBorderIterator( root ); // Find the weakest of the cluster borders: Country weakest = null; int weakestArmies = 1000000; while (borders.hasNext()) { Country border = borders.next(); if (border.getArmies() < weakestArmies) { weakestArmies = border.getArmies(); weakest = border; } } if (weakest == null) System.out.println("SmartBase.placeArmiesOnClusterBorder() -> weakest==null. 7404524"); int numberToPlace = Math.min(numberOfArmies, Math.max(1, numberOfArmies/100)); board.placeArmies( numberToPlace, weakest ); if (numberOfArmies > numberToPlace) placeArmiesOnClusterBorder( numberOfArmies-numberToPlace, root ); } /*********** The actual attack methods are below. Different skill level of agents can use different combinations of them. ************/// If any of our border countries around <root>'s cluster have only one enemy then attack it // (if they have some chance of winning)// return true if we won at least one attack protected boolean attackEasyExpand(Country root) { // get the borders of the cluster centered on <root>: CountryIterator borders = new ClusterBorderIterator( root ); boolean wonAttack = false; while (borders.hasNext()) { Country border = borders.next(); CountryIterator neighbors = new NeighborIterator(border); int enemies = 0; Country enemy = null; while (neighbors.hasNext()) { Country neighbor = neighbors.next(); if (neighbor.getOwner() != ID) { enemies++; enemy = neighbor; } } if (enemies == 1 && border.getArmies() > enemy.getArmies()) { // then we will attack that one country and move everything in, thus expanding our borders. moveInMemory = 1000000; if (board.attack( border, enemy, true) > 0) wonAttack = true; moveInMemory = -1; } } return wonAttack; }// Attack any countries next to <root>'s cluster that has zero not-owned-by-us neighbors// This kills little islands to fill out our territory.// return true if we won at least one attack protected boolean attackFillOut(Country root ) { boolean wonAttack = false; CountryIterator borders = new ClusterBorderIterator( root ); while (borders.hasNext()) { Country border = borders.next(); CountryIterator neighbors = new NeighborIterator(border); while (neighbors.hasNext()) { Country neighbor = neighbors.next(); if (neighbor.getOwner() != ID && neighbor.getNumberNotPlayerNeighbors(ID) == 0) { // attack it if (neighbor.getArmies() < border.getArmies()) { moveInMemory = 0; // since we are attacking from a border we remember to move zero armies in if (board.attack( border, neighbor, true) == 7) wonAttack = true; moveInMemory = -1; } } } } return wonAttack; }// If we can, we consolidate our borders, by attacking from two or more borderCountries into a common enemy// return true if we won at least one attackprotected boolean attackConsolidate( Country root) { CountryIterator borders = new ClusterBorderIterator( root ); boolean wonAttack = false; while (borders.hasNext()) { Country border = borders.next(); CountryIterator neighbors = new NeighborIterator(border); int enemies = 0; Country enemy = null; while (neighbors.hasNext()) { Country neighbor = neighbors.next(); if (neighbor.getOwner() != ID) { enemies++; enemy = neighbor; } } if (enemies == 1 && enemy.getNumberPlayerNeighbors(ID) > 1) { // then this enemy could be a good point to consolidate. // look for other border countries to consolidate into enemy... List ours = new ArrayList(); // this will store all the countries that will participate in the attack. CountryIterator possibles = new NeighborIterator(enemy); while (possibles.hasNext() )
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -