Merge branch 'master' of https://gitlab.com/joachimschmidt557/fop-projekt
This commit is contained in:
commit
d0aa8fda5d
2 changed files with 147 additions and 94 deletions
|
|
@ -13,53 +13,68 @@ import game.AI;
|
||||||
import game.Game;
|
import game.Game;
|
||||||
import game.Player;
|
import game.Player;
|
||||||
import game.map.Castle;
|
import game.map.Castle;
|
||||||
|
import game.map.PathFinding;
|
||||||
import gui.AttackThread;
|
import gui.AttackThread;
|
||||||
|
import gui.components.MapPanel.Action;
|
||||||
|
|
||||||
public class StrongAI extends AI {
|
public class StrongAI extends AI {
|
||||||
|
|
||||||
private static boolean DEBUG = false;
|
|
||||||
|
|
||||||
private static double UTILITY_F1 = 3.0;
|
// These values were determined mostly by trial-and-error
|
||||||
private static double UTILITY_F2 = 5.0;
|
|
||||||
private static double UTILITY_F3 = 1.0;
|
|
||||||
private static double UTILITY_F4 = -10.0;
|
|
||||||
private static double UTILITY_F5 = 3.0;
|
|
||||||
private static double UTILITY_F6 = 2.0;
|
|
||||||
|
|
||||||
private static double REINFORCE_F1 = 1.0;
|
private static double UTILITY_F1 = 4.0; // Number of adjacent friendly castles
|
||||||
private static double REINFORCE_F2 = -5.0;
|
private static double UTILITY_F2 = 1.0; // Number of adjacent enemy castles
|
||||||
private static double REINFORCE_F3 = 0.5;
|
private static double UTILITY_F3 = 3.0; // Number of connected edges
|
||||||
private static double REINFORCE_F4 = -100.0;
|
private static double UTILITY_F4 = -10.0; // Is surrounded by opponents castles
|
||||||
private static double REINFORCE_F5 = -2.0;
|
private static double UTILITY_F5 = 3.0; // Size of the castles batch
|
||||||
|
private static double UTILITY_F6 = 4.0; // Castles missing to a full kingdom
|
||||||
|
|
||||||
private static double ATTACK_F1 = 1.0;
|
private static double REINFORCE_F1 = 1.0; // Number of adjacent enemy castles
|
||||||
|
private static double REINFORCE_F2 = -5.0; // Is surrounded by opponents castles
|
||||||
|
private static double REINFORCE_F3 = 0.5; // Castle utility
|
||||||
|
private static double REINFORCE_F4 = -100.0; // Is border castle
|
||||||
|
private static double REINFORCE_F5 = -2.0; // Number of troops
|
||||||
|
|
||||||
private static double EVAL_CASTLES = 1.0;
|
private static double ATTACK_F1 = 1.0; // Castle utility
|
||||||
|
|
||||||
|
private static double EVAL_OWN = 3.0; // Weight of own utilities
|
||||||
|
private static double EVAL_OPP = -10.0; // Weight of opponents utilities
|
||||||
|
|
||||||
|
private boolean playedJoker1 = false;
|
||||||
|
private boolean playedJoker2 = false;
|
||||||
|
|
||||||
|
|
||||||
|
public StrongAI(String name, Color color) {
|
||||||
|
super(name, color);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the current game state
|
* Evaluates the current game state
|
||||||
* @param game the current game
|
* @param game the current game
|
||||||
* @param graph the graph to be evaluated
|
* @param graph the graph to be evaluated
|
||||||
* @param p the player
|
* @param p the player
|
||||||
|
* @param change the castle that should be considered to belong to p
|
||||||
* @return the state value
|
* @return the state value
|
||||||
*/
|
*/
|
||||||
public static double evaluateState(Game game, Graph<Castle> g, Player p, Optional<Castle> change) {
|
public static double evaluateState(Game game, Graph<Castle> g, Player p, Optional<Castle> change) {
|
||||||
|
// Temporarily give castle "change" to p
|
||||||
Player owner = null;
|
Player owner = null;
|
||||||
if(change.isPresent()) {
|
if(change.isPresent()) {
|
||||||
owner = change.get().getOwner();
|
owner = change.get().getOwner();
|
||||||
change.get().setOwner(p);
|
change.get().setOwner(p);
|
||||||
}
|
}
|
||||||
|
// Calculate total state utility
|
||||||
double value = 0;
|
double value = 0;
|
||||||
for(Player player : game.getPlayers()) {
|
for(Player player : game.getPlayers()) {
|
||||||
List<Castle> castles = g.getAllValues().stream()
|
List<Castle> castles = g.getAllValues().stream()
|
||||||
.filter(x->x.getOwner() == player)
|
.filter(x->x.getOwner() == player)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
double s = (p == player)? 3.0 : -10.0;
|
double s = (p == player)? EVAL_OWN : EVAL_OPP;
|
||||||
for(Castle c : castles) {
|
for(Castle c : castles) {
|
||||||
value += s * utilityCastle(g, player, c) * EVAL_CASTLES;
|
value += s * utilityCastle(g, player, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Hand "change" back to its previous owner
|
||||||
if(change.isPresent()) {
|
if(change.isPresent()) {
|
||||||
change.get().setOwner(owner);
|
change.get().setOwner(owner);
|
||||||
}
|
}
|
||||||
|
|
@ -104,21 +119,7 @@ public class StrongAI extends AI {
|
||||||
// Castles missing to a full kingdom
|
// Castles missing to a full kingdom
|
||||||
double f6 = c.getKingdom().getCastles().stream()
|
double f6 = c.getKingdom().getCastles().stream()
|
||||||
.filter(x->x.getOwner() == p).count() - c.getKingdom().getCastles().size() + 2;
|
.filter(x->x.getOwner() == p).count() - c.getKingdom().getCastles().size() + 2;
|
||||||
|
|
||||||
if(DEBUG) {
|
|
||||||
System.out.println("-----");
|
|
||||||
System.out.println("Castle: " + c.getName());
|
|
||||||
System.out.println("F1: " + f1 * UTILITY_F1);
|
|
||||||
System.out.println("F2: " + f2 * UTILITY_F2);
|
|
||||||
System.out.println("F3: " + f3 * UTILITY_F3);
|
|
||||||
System.out.println("F4: " + f4 * UTILITY_F4);
|
|
||||||
System.out.println("F5: " + f5 * UTILITY_F5);
|
|
||||||
System.out.println("F6: " + f6 * UTILITY_F6);
|
|
||||||
System.out.println("Total: " + (UTILITY_F1 * f1 + UTILITY_F2 * f2
|
|
||||||
+ UTILITY_F3 * f3 + UTILITY_F4 * f4 + f5 * UTILITY_F5
|
|
||||||
+ f6 * UTILITY_F6));
|
|
||||||
System.out.println("-----");
|
|
||||||
}
|
|
||||||
return UTILITY_F1 * f1 + UTILITY_F2 * f2
|
return UTILITY_F1 * f1 + UTILITY_F2 * f2
|
||||||
+ UTILITY_F3 * f3 + UTILITY_F4 * f4 + f5 * UTILITY_F5
|
+ UTILITY_F3 * f3 + UTILITY_F4 * f4 + f5 * UTILITY_F5
|
||||||
+ f6 * UTILITY_F6;
|
+ f6 * UTILITY_F6;
|
||||||
|
|
@ -126,10 +127,10 @@ public class StrongAI extends AI {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Analyzes the utility to attack a certain castle
|
* Analyzes the utility to attack a certain castle
|
||||||
* @param g
|
* @param g the graph
|
||||||
* @param p
|
* @param p the current player
|
||||||
* @param c
|
* @param c the castle
|
||||||
* @return
|
* @return the reinforcement utility
|
||||||
*/
|
*/
|
||||||
public static double utilityReinforce(Graph<Castle> g, Player p, Castle c) {
|
public static double utilityReinforce(Graph<Castle> g, Player p, Castle c) {
|
||||||
Node<Castle> node = g.getNode(c);
|
Node<Castle> node = g.getNode(c);
|
||||||
|
|
@ -152,21 +153,9 @@ public class StrongAI extends AI {
|
||||||
// Is border castle
|
// Is border castle
|
||||||
double f4 = isBorder(g, c)? 0.0 : 1.0;
|
double f4 = isBorder(g, c)? 0.0 : 1.0;
|
||||||
|
|
||||||
// Number of troops exceeding 4
|
// Number of troops exceeding
|
||||||
double f5 = c.getTroopCount()-4;
|
double f5 = c.getTroopCount();
|
||||||
|
|
||||||
if(DEBUG) {
|
|
||||||
System.out.println("-----");
|
|
||||||
System.out.println("Castle: " + c.getName());
|
|
||||||
System.out.println("F1: " + f1 * REINFORCE_F1);
|
|
||||||
System.out.println("F2: " + f2 * REINFORCE_F2);
|
|
||||||
System.out.println("F3: " + f3 * REINFORCE_F3);
|
|
||||||
System.out.println("F4: " + f4 * REINFORCE_F4);
|
|
||||||
System.out.println("F5: " + f5 * REINFORCE_F5);
|
|
||||||
System.out.println("Total: " + (REINFORCE_F1 * f1 + REINFORCE_F2 * f2 + f3 * REINFORCE_F3
|
|
||||||
+ f4 * REINFORCE_F4 + f5 * REINFORCE_F5));
|
|
||||||
System.out.println("-----");
|
|
||||||
}
|
|
||||||
return REINFORCE_F1 * f1 + REINFORCE_F2 * f2 + f3 * REINFORCE_F3
|
return REINFORCE_F1 * f1 + REINFORCE_F2 * f2 + f3 * REINFORCE_F3
|
||||||
+ f4 * REINFORCE_F4 + f5 * REINFORCE_F5;
|
+ f4 * REINFORCE_F4 + f5 * REINFORCE_F5;
|
||||||
}
|
}
|
||||||
|
|
@ -184,40 +173,11 @@ public class StrongAI extends AI {
|
||||||
|
|
||||||
double ratio = ((double)a.getTroopCount())/t.getTroopCount();
|
double ratio = ((double)a.getTroopCount())/t.getTroopCount();
|
||||||
|
|
||||||
return ratio * (ATTACK_F1 * f1);
|
return ATTACK_F1 * f1 * ratio;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a batch of connected castles around some start castle that belong to the player p
|
* Chooses the initial castles in round one
|
||||||
* @param g the graph
|
|
||||||
* @param p the owner of the castles
|
|
||||||
* @param castle the start castle
|
|
||||||
* @param checked a list of checked castles, initialize as new ArrayList
|
|
||||||
* @return a list of castles
|
|
||||||
*/
|
|
||||||
public static List<Castle> getBatch(Graph<Castle> g, Player p, Castle castle, List<Castle> checked) {
|
|
||||||
Node<Castle> n = g.getNode(castle);
|
|
||||||
Set<Castle> adjacent = g.getEdges(n).stream()
|
|
||||||
.map(x->x.getOtherNode(n).getValue())
|
|
||||||
.filter(x->x.getOwner() == p)
|
|
||||||
.filter(x->checked.contains(x) == false)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
if(!checked.contains(castle))
|
|
||||||
checked.add(castle);
|
|
||||||
for(Castle c : adjacent) {
|
|
||||||
checked.addAll(getBatch(g, p, c, checked).stream()
|
|
||||||
.filter(x->checked.contains(x) == false)
|
|
||||||
.collect(Collectors.toSet()));
|
|
||||||
}
|
|
||||||
return checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
public StrongAI(String name, Color color) {
|
|
||||||
super(name, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Chooses the initial castles in round 1
|
|
||||||
* @param game the game
|
* @param game the game
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
*/
|
*/
|
||||||
|
|
@ -227,15 +187,12 @@ public class StrongAI extends AI {
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
while(availableCastles.size() > 0 && getRemainingTroops() > 0) {
|
while(availableCastles.size() > 0 && getRemainingTroops() > 0) {
|
||||||
|
sleep(1000);
|
||||||
sleep(3);
|
|
||||||
|
|
||||||
Castle best = availableCastles.get(0);
|
Castle best = availableCastles.get(0);
|
||||||
double bestScore = 0;
|
double bestScore = 0;
|
||||||
double score = 0;
|
double score = 0;
|
||||||
for(Castle c : availableCastles) {
|
for(Castle c : availableCastles) {
|
||||||
score = evaluateState(game, game.getMap().getGraph(), this, Optional.of(c));
|
score = evaluateState(game, game.getMap().getGraph(), this, Optional.of(c));
|
||||||
//score = evaluateLookahead(game, game.getMap().getGraph(), this, 10);
|
|
||||||
if(score > bestScore) {
|
if(score > bestScore) {
|
||||||
bestScore = score;
|
bestScore = score;
|
||||||
best = c;
|
best = c;
|
||||||
|
|
@ -256,6 +213,7 @@ public class StrongAI extends AI {
|
||||||
List<Castle> castles = game.getMap().getCastles().stream()
|
List<Castle> castles = game.getMap().getCastles().stream()
|
||||||
.filter(x->x.getOwner() == this)
|
.filter(x->x.getOwner() == this)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
while(this.getRemainingTroops() > 0) {
|
while(this.getRemainingTroops() > 0) {
|
||||||
Castle best = castles.get(0);
|
Castle best = castles.get(0);
|
||||||
double bestScore = 0;
|
double bestScore = 0;
|
||||||
|
|
@ -266,7 +224,7 @@ public class StrongAI extends AI {
|
||||||
best = c;
|
best = c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sleep(5);
|
sleep(500);
|
||||||
game.addTroops(this, best, 1);
|
game.addTroops(this, best, 1);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -295,12 +253,16 @@ public class StrongAI extends AI {
|
||||||
while(d.getTroopCount() > 1) {
|
while(d.getTroopCount() > 1) {
|
||||||
double bestScore = 0;
|
double bestScore = 0;
|
||||||
Castle best = receivers.get(0);
|
Castle best = receivers.get(0);
|
||||||
|
// Do not cheat like evil BasicAI,
|
||||||
|
// only move troops if there is a path
|
||||||
|
PathFinding path = new PathFinding(g, d, Action.MOVING, this);
|
||||||
for(Castle c : receivers) {
|
for(Castle c : receivers) {
|
||||||
double score = utilityReinforce(g, this, c);
|
double score = utilityReinforce(g, this, c);
|
||||||
if(score > bestScore) {
|
if(score > bestScore && path.getPath(c) != null) {
|
||||||
bestScore = score;
|
if(path.getPath(c).size() > 0) {
|
||||||
best = c;
|
bestScore = score;
|
||||||
|
best = c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(best == d) break;
|
if(best == d) break;
|
||||||
|
|
@ -342,7 +304,8 @@ public class StrongAI extends AI {
|
||||||
best = t;
|
best = t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(bestScore > evaluateState(game, g, this, Optional.empty())) {
|
// Attack, if the chance of taking the castle is more useful than not attacking
|
||||||
|
if(bestScore > evaluateState(game, g, this, Optional.empty()) && a.getTroopCount() > best.getTroopCount()) {
|
||||||
AttackThread attackThread = game.startAttack(a, best, a.getTroopCount());
|
AttackThread attackThread = game.startAttack(a, best, a.getTroopCount());
|
||||||
if(fastForward)
|
if(fastForward)
|
||||||
attackThread.fastForward();
|
attackThread.fastForward();
|
||||||
|
|
@ -357,20 +320,38 @@ public class StrongAI extends AI {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Play some jokers
|
||||||
|
* @param game the current game
|
||||||
|
*/
|
||||||
|
private void playJokers(Game game) {
|
||||||
|
if(!playedJoker1) {
|
||||||
|
if(this.getCastles(game).size() <= game.getMap().getCastles().size()/2) {
|
||||||
|
playedJoker1 = true;
|
||||||
|
System.out.println("StrongAI played joker 1.");
|
||||||
|
this.addTroops(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!playedJoker2) {
|
||||||
|
// TODO: Play joker 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void actions(Game game) throws InterruptedException {
|
protected void actions(Game game) throws InterruptedException {
|
||||||
if(game.getRound() == 1) {
|
if(game.getRound() == 1) {
|
||||||
chooseInitialCastles(game);
|
chooseInitialCastles(game);
|
||||||
}else {
|
}else {
|
||||||
|
playJokers(game);
|
||||||
distributeTroops(game);
|
distributeTroops(game);
|
||||||
boolean shouldAttack = false;
|
boolean shouldAttack = false;
|
||||||
do {
|
do {
|
||||||
//fastForward();
|
|
||||||
shouldAttack = attackCastles(game);
|
shouldAttack = attackCastles(game);
|
||||||
reinforceCastles(game);
|
reinforceCastles(game);
|
||||||
}while(shouldAttack);
|
}while(shouldAttack);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks, whether a given castle has adjacent opponent castles
|
* Checks, whether a given castle has adjacent opponent castles
|
||||||
* @param g the graph
|
* @param g the graph
|
||||||
|
|
@ -386,5 +367,29 @@ public class StrongAI extends AI {
|
||||||
|
|
||||||
return (a > 0)? true : false;
|
return (a > 0)? true : false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a batch of connected castles around some start castle that belong to the player p
|
||||||
|
* @param g the graph
|
||||||
|
* @param p the owner of the castles
|
||||||
|
* @param castle the start castle
|
||||||
|
* @param checked a list of checked castles, initialize as new ArrayList
|
||||||
|
* @return a list of castles
|
||||||
|
*/
|
||||||
|
public static List<Castle> getBatch(Graph<Castle> g, Player p, Castle castle, List<Castle> checked) {
|
||||||
|
Node<Castle> n = g.getNode(castle);
|
||||||
|
Set<Castle> adjacent = g.getEdges(n).stream()
|
||||||
|
.map(x->x.getOtherNode(n).getValue())
|
||||||
|
.filter(x->x.getOwner() == p)
|
||||||
|
.filter(x->checked.contains(x) == false)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if(!checked.contains(castle))
|
||||||
|
checked.add(castle);
|
||||||
|
for(Castle c : adjacent) {
|
||||||
|
checked.addAll(getBatch(g, p, c, checked).stream()
|
||||||
|
.filter(x->checked.contains(x) == false)
|
||||||
|
.collect(Collectors.toSet()));
|
||||||
|
}
|
||||||
|
return checked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,55 @@ $$\Theta(n^4)$$
|
||||||
\section{Weitergestaltung des Spiels}
|
\section{Weitergestaltung des Spiels}
|
||||||
|
|
||||||
\subsection{Computergegner}
|
\subsection{Computergegner}
|
||||||
|
\subsubsection{Funktionsweise}
|
||||||
|
Die hier verwendete Implementation ist eine \textit{Utility Based AI}. Die Funktionalität beruht hauptsächlich auf mehreren \textit{Utility-Funktionen}, welche die Nützlichkeit einer bestimmten Aktion bewerten.
|
||||||
|
|
||||||
|
Der Computer prüft verschiedene verfügbare Züge, bestimmt jeweils deren \textit{utility} und wählt den besten aus, wobei darauf geachtet wird, dass der Zug dem Computer den größtmöglichen Vorteil und dem Gegner den größtmöglichen Nachteil verschafft.
|
||||||
|
|
||||||
|
In der ersten Runde nutzt der Computer die \texttt{evaluateState} Funktion (siehe 2.1.2), um immer die beste verfügbare Burg, in Abhängigkeit der bereits gewählten Burgen zu bestimmen.
|
||||||
|
|
||||||
|
Ab der zweiten Runde werden, falls sinnvoll, zunächst Joker gespielt und anschließend die neuen Truppen auf die Burgen verteilt, wobei mit der Funktion \texttt{utilityReinforce} bestimmt wird, welche Burgen am nötigsten Truppen brauchen.
|
||||||
|
|
||||||
|
Danach beginnt die Eroberungsphase der Runde: mithilfe der \texttt{utilityCastle}-Funktion wird die wertvollste gegnerische Burg bestimmt und angegriffen, sofern der Angriff für den Computer sinnvoll ist.
|
||||||
|
|
||||||
|
Nach jedem Angriff werden die Truppen zu den Burgen am Rand umverteilt, wobei wieder nach \textit{utility} vorgegangen wird. Die Angriffsphase geht solange, wie es noch Burgen gibt, die sinnvoll angegriffen werden können.
|
||||||
|
|
||||||
|
\subsubsection{Utility-Funktionen}
|
||||||
|
|
||||||
|
Es werden drei verschiedene Utility-Funktionen verwendet, um die Nützlichkeit einer bestimmten Aktion zu bewerten: \texttt{utilityCastle}, \texttt{utilityReinforce} und \texttt{utilityAttack}, sowie eine vierte Funktion \texttt{evaluateState}, die den gesamten Spielzustand bewertet.
|
||||||
|
Bei den ersten drei Funktionen handelt es sich um lineare Polynome der Form
|
||||||
|
|
||||||
|
\begin{center}
|
||||||
|
\begin{large}
|
||||||
|
$\sum \limits_{k=1}^{n} \alpha_k f_k$
|
||||||
|
\end{large}
|
||||||
|
\end{center}
|
||||||
|
|
||||||
|
wobei $f_k$ wiederum Funktionen sind, welche jeweils einen Aspekt der Bewertung betrachten, mehr dazu unten.
|
||||||
|
|
||||||
|
Bei den Koeffizienten $\alpha_k$ handelt es sich um um \texttt{double}-Zahlen, welche die einzelnen Funktionen gewichten. Die drei Utility-Funktionen unterscheiden sich in den jeweils betrachteten Aspekten.
|
||||||
|
|
||||||
|
Die Funktion \texttt{evaluateState} berechnet den kumulierten Wert aller Burgen für den Computer und subtrahiert einen gewichteten Wert der Burgen für den Gegner. Sie bietet also eine Möglichkeit, den gesamten Wert eines Spielzustandes zu bestimmen.
|
||||||
|
\\\\
|
||||||
|
\textbf{utilityCastle}\\\\
|
||||||
|
Diese Funktion bestimmt den "Wert" einer Burg für einen bestimmten Spieler. Hierbei fließen die Anzahl der angrenzenden eigenen und gegnerischen Burgen, die Zahl der abgehenden Kanten, die Größe des Blocks von zusammenhängenden eigenen Burgen, in dem sich die Burg befindet und weitere Parameter ein. Diese werden gewichtet und addiert, um einen heuristischen \textit{utility}-Wert zu erzeugen.
|
||||||
|
|
||||||
|
Eine Burg wird als "wertvoll" betrachtet, wenn sie an einer strategisch günstigen Position ist, und durch andere Burgen unterstützt werden kann. Dies soll dann durch einen hohen Ausgabewert ausgedrückt werden.
|
||||||
|
\\\\
|
||||||
|
\textbf{utilityReinforce}\\\\
|
||||||
|
Diese Funktion gibt an, wie sinnvoll es ist, eine bestimmte Burg mit Truppen zu versorgen. Hierbei spielen die Zahl der angrenzenden gegnerischen Burgen, die Zahl der Truppen in der Burg, der Wert der Burg und weitere Parameter eine Rolle.
|
||||||
|
Generell sollen Burgen unterstützt werden, die entweder durch viele starke gegnerische Burgen gefährdet sind, oder für einen taktischen Angriff verwendet werden können.
|
||||||
|
\\\\
|
||||||
|
\textbf{utilityAttack}\\\\
|
||||||
|
Diese Funktion gibt an, wie sinnvoll es ist, eine bestimmte gegnerische Burg anzugreifen. Hier fließen der Wert der Burg für den Computer und für den Gegner und das Truppenverhältnis von Angreifer- zu Zielburg ein.
|
||||||
|
|
||||||
|
Burgen sollten dann angegriffen werden, wenn ihre Eroberung den Gesamtspielzustand zugunsten des Computers verändert und der Angriff eine große Erfolgschance hat.
|
||||||
|
|
||||||
|
\subsubsection{Motivation und Anmerkung}
|
||||||
|
|
||||||
|
Die \textit{Utility Based AI} ist ein häufig verwendeter und erfolgreicher Ansatz zur Entwicklung von Computergegnern für Strategiespiele. Die Umsetzung ist inspiriert von Artikeln wie "\textit{An Introduction to Utility Theory}" von David Graham und "\textit{Designing AI Algorithms For Turn-Based Strategy Games}" von Ed Welch, wurde aber vollständig selbst durch Trial-And-Error implementiert und optimiert. Sie ist der \textit{Basic AI} überlegen, hat aber noch Raum für Verbesserung.
|
||||||
|
|
||||||
|
Ein möglicher Verbesserungsvorschlag wäre, den Computer mehrere Züge in die Zukunft zu planen zu lassen und somit intelligenteres, strategisches Vorgehen zu ermöglichen (beispielsweise durch Minimax und co.).
|
||||||
\subsection{Missionen}
|
\subsection{Missionen}
|
||||||
|
|
||||||
\subsubsection{Begrenzte Rundenanzahl}
|
\subsubsection{Begrenzte Rundenanzahl}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue