Real rename
This commit is contained in:
parent
aa3dd87076
commit
cafb36cb26
68 changed files with 0 additions and 0 deletions
72
Projektgruppe_175/src/game/AI.java
Normal file
72
Projektgruppe_175/src/game/AI.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package game;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.Random;
|
||||
|
||||
public abstract class AI extends Player {
|
||||
|
||||
private AIThread aiThread;
|
||||
private Random random;
|
||||
protected boolean fastForward;
|
||||
|
||||
public AI(String name, Color color) {
|
||||
super(name, color);
|
||||
this.random = new Random();
|
||||
}
|
||||
|
||||
protected Random getRandom() {
|
||||
return this.random;
|
||||
}
|
||||
|
||||
protected abstract void actions(Game game) throws InterruptedException;
|
||||
|
||||
public void doNextTurn(Game game) {
|
||||
if(aiThread != null)
|
||||
return;
|
||||
|
||||
fastForward = false;
|
||||
aiThread = new AIThread(game);
|
||||
aiThread.start();
|
||||
}
|
||||
|
||||
public void fastForward() {
|
||||
if(aiThread != null)
|
||||
fastForward = true;
|
||||
}
|
||||
|
||||
protected void sleep(int ms) throws InterruptedException {
|
||||
long end = System.currentTimeMillis() + ms;
|
||||
while(System.currentTimeMillis() < end && !fastForward) {
|
||||
Thread.sleep(10);
|
||||
}
|
||||
}
|
||||
|
||||
private class AIThread extends Thread {
|
||||
|
||||
private Game game;
|
||||
|
||||
private AIThread(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
private void finishTurn() {
|
||||
aiThread = null;
|
||||
fastForward = false;
|
||||
|
||||
// Trigger next round, if not automatically
|
||||
if(game.getRound() > 1 && game.getCurrentPlayer() == AI.this)
|
||||
game.nextTurn();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
actions(game);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
finishTurn();
|
||||
}
|
||||
}
|
||||
}
|
||||
293
Projektgruppe_175/src/game/Game.java
Normal file
293
Projektgruppe_175/src/game/Game.java
Normal file
|
|
@ -0,0 +1,293 @@
|
|||
package game;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import game.map.Castle;
|
||||
import game.map.Kingdom;
|
||||
import game.map.GameMap;
|
||||
import game.map.MapSize;
|
||||
import gui.AttackThread;
|
||||
import gui.Resources;
|
||||
|
||||
public class Game {
|
||||
|
||||
private Goal goal;
|
||||
private List<Player> players;
|
||||
private boolean isOver;
|
||||
private boolean hasStarted;
|
||||
private int round;
|
||||
private MapSize mapSize;
|
||||
private GameMap gameMap;
|
||||
private Queue<Player> playerQueue;
|
||||
private Player startingPlayer;
|
||||
private Player currentPlayer;
|
||||
private GameInterface gameInterface;
|
||||
private AttackThread attackThread;
|
||||
|
||||
public Game() {
|
||||
this.isOver = false;
|
||||
this.hasStarted = false;
|
||||
this.mapSize = MapSize.MEDIUM;
|
||||
this.players = new LinkedList<>();
|
||||
}
|
||||
|
||||
public void addPlayer(Player p) {
|
||||
if(players.contains(p))
|
||||
throw new IllegalArgumentException("Spieler wurde bereits hinzugefügt");
|
||||
|
||||
this.players.add(p);
|
||||
}
|
||||
|
||||
public void setGoal(Goal goal) {
|
||||
this.goal = goal;
|
||||
this.goal.setGame(this);
|
||||
}
|
||||
|
||||
public int getRound() {
|
||||
return round;
|
||||
}
|
||||
|
||||
public void setMapSize(MapSize mapSize) {
|
||||
this.mapSize = mapSize;
|
||||
}
|
||||
|
||||
private void generateMap() {
|
||||
|
||||
int mapSizeMultiplier = this.mapSize.ordinal() + 1;
|
||||
int playerCount = players.size();
|
||||
int numRegions = playerCount * GameConstants.CASTLES_NUMBER_MULTIPLIER * mapSizeMultiplier;
|
||||
double tileMultiplier = 1.0 + (mapSizeMultiplier * 0.3);
|
||||
|
||||
// We set up space for 2 times the region count
|
||||
int numTiles = (int) Math.ceil(numRegions * tileMultiplier);
|
||||
|
||||
// Our map should be 3:2
|
||||
int width = (int) Math.ceil(0.6 * numTiles);
|
||||
int height = (int) Math.ceil(0.4 * numTiles);
|
||||
|
||||
int continents = Math.min(3, playerCount + this.mapSize.ordinal());
|
||||
|
||||
this.gameMap = GameMap.generateRandomMap(width, height, 40, numRegions, continents);
|
||||
}
|
||||
|
||||
public void start(GameInterface gameInterface) {
|
||||
|
||||
if(hasStarted)
|
||||
throw new IllegalArgumentException("Spiel wurde bereits gestartet");
|
||||
|
||||
if(players.size() < 2)
|
||||
throw new IllegalArgumentException("Nicht genug Spieler");
|
||||
|
||||
if(goal == null)
|
||||
throw new IllegalArgumentException("Kein Spielziel gesetzt");
|
||||
|
||||
this.generateMap();
|
||||
|
||||
// Create random player order
|
||||
this.gameInterface = gameInterface;
|
||||
List<Player> tempList = new ArrayList<>(players);
|
||||
playerQueue = new ArrayDeque<>();
|
||||
while(!tempList.isEmpty()) {
|
||||
Player player = tempList.remove((int) (Math.random() * tempList.size()));
|
||||
player.reset();
|
||||
playerQueue.add(player);
|
||||
}
|
||||
|
||||
startingPlayer = playerQueue.peek();
|
||||
hasStarted = true;
|
||||
isOver = false;
|
||||
round = 0;
|
||||
|
||||
gameInterface.onGameStarted(this);
|
||||
nextTurn();
|
||||
}
|
||||
|
||||
public AttackThread startAttack(Castle source, Castle target, int troopCount) {
|
||||
if(attackThread != null)
|
||||
return attackThread;
|
||||
|
||||
if(source.getOwner() == target.getOwner() || troopCount < 1)
|
||||
return null;
|
||||
|
||||
attackThread = new AttackThread(this, source, target, troopCount);
|
||||
attackThread.start();
|
||||
gameInterface.onAttackStarted(source, target, troopCount);
|
||||
return attackThread;
|
||||
}
|
||||
|
||||
public void doAttack(Castle attackerCastle, Castle defenderCastle, int[] rollAttacker, int[] rollDefender) {
|
||||
|
||||
Integer[] rollAttackerSorted = Arrays.stream(rollAttacker).boxed().sorted(Comparator.reverseOrder()).toArray(Integer[]::new);
|
||||
Integer[] rollDefenderSorted = Arrays.stream(rollDefender).boxed().sorted(Comparator.reverseOrder()).toArray(Integer[]::new);
|
||||
|
||||
Player attacker = attackerCastle.getOwner();
|
||||
Player defender = defenderCastle.getOwner();
|
||||
|
||||
for(int i = 0; i < Math.min(rollAttacker.length, rollDefender.length); i++) {
|
||||
if(rollAttackerSorted[i] > rollDefenderSorted[i]) {
|
||||
defenderCastle.removeTroops(1);
|
||||
if(defenderCastle.getTroopCount() == 0) {
|
||||
attackerCastle.removeTroops(1);
|
||||
defenderCastle.setOwner(attacker);
|
||||
defenderCastle.addTroops(1);
|
||||
gameInterface.onConquer(defenderCastle, attacker);
|
||||
addScore(attacker, 50);
|
||||
break;
|
||||
} else {
|
||||
addScore(attacker, 20);
|
||||
}
|
||||
} else {
|
||||
attackerCastle.removeTroops(1);
|
||||
addScore(defender, 30);
|
||||
}
|
||||
}
|
||||
|
||||
gameInterface.onUpdate();
|
||||
}
|
||||
|
||||
public void moveTroops(Castle source, Castle destination, int troopCount) {
|
||||
if(troopCount >= source.getTroopCount() || source.getOwner() != destination.getOwner())
|
||||
return;
|
||||
|
||||
source.moveTroops(destination, troopCount);
|
||||
gameInterface.onUpdate();
|
||||
}
|
||||
|
||||
public void stopAttack() {
|
||||
this.attackThread = null;
|
||||
this.gameInterface.onAttackStopped();
|
||||
}
|
||||
|
||||
public int[] roll(Player player, int dices, boolean fastForward) {
|
||||
return gameInterface.onRoll(player, dices, fastForward);
|
||||
}
|
||||
|
||||
private boolean allCastlesChosen() {
|
||||
return gameMap.getCastles().stream().noneMatch(c -> c.getOwner() == null);
|
||||
}
|
||||
|
||||
public AttackThread getAttackThread() {
|
||||
return this.attackThread;
|
||||
}
|
||||
|
||||
public void chooseCastle(Castle castle, Player player) {
|
||||
if(castle.getOwner() != null || player.getRemainingTroops() == 0)
|
||||
return;
|
||||
|
||||
gameInterface.onCastleChosen(castle, player);
|
||||
player.removeTroops(1);
|
||||
castle.setOwner(currentPlayer);
|
||||
castle.addTroops(1);
|
||||
addScore(player, 5);
|
||||
|
||||
if(player.getRemainingTroops() == 0 || allCastlesChosen()) {
|
||||
player.removeTroops(player.getRemainingTroops());
|
||||
nextTurn();
|
||||
}
|
||||
}
|
||||
|
||||
public void addTroops(Player player, Castle castle, int count) {
|
||||
if(count < 1 || castle.getOwner() != player)
|
||||
return;
|
||||
|
||||
count = Math.min(count, player.getRemainingTroops());
|
||||
castle.addTroops(count);
|
||||
player.removeTroops(count);
|
||||
}
|
||||
|
||||
public void addScore(Player player, int score) {
|
||||
player.addPoints(score);
|
||||
gameInterface.onAddScore(player, score);
|
||||
}
|
||||
|
||||
public void endGame() {
|
||||
isOver = true;
|
||||
Player winner = goal.getWinner();
|
||||
|
||||
if(winner != null)
|
||||
addScore(goal.getWinner(), 150);
|
||||
|
||||
Resources resources = Resources.getInstance();
|
||||
for(Player player : players) {
|
||||
resources.addScoreEntry(new ScoreEntry(player, goal));
|
||||
}
|
||||
|
||||
gameInterface.onGameOver(winner);
|
||||
}
|
||||
|
||||
public void nextTurn() {
|
||||
|
||||
if(goal.isCompleted()) {
|
||||
endGame();
|
||||
return;
|
||||
}
|
||||
|
||||
// Choose next player
|
||||
Player nextPlayer;
|
||||
do {
|
||||
nextPlayer = playerQueue.remove();
|
||||
|
||||
// if player has already lost, remove him from queue
|
||||
if(goal.hasLost(nextPlayer)) {
|
||||
if(startingPlayer == nextPlayer) {
|
||||
startingPlayer = playerQueue.peek();
|
||||
}
|
||||
nextPlayer = null;
|
||||
}
|
||||
} while(nextPlayer == null && !playerQueue.isEmpty());
|
||||
|
||||
if(nextPlayer == null) {
|
||||
isOver = true;
|
||||
gameInterface.onGameOver(goal.getWinner());
|
||||
return;
|
||||
}
|
||||
|
||||
currentPlayer = nextPlayer;
|
||||
if(round == 0 || (round == 1 && allCastlesChosen()) || (round > 1 && currentPlayer == startingPlayer)) {
|
||||
round++;
|
||||
gameInterface.onNewRound(round);
|
||||
}
|
||||
|
||||
int numRegions = currentPlayer.getNumRegions(this);
|
||||
|
||||
int addTroops;
|
||||
if(round == 1)
|
||||
addTroops = GameConstants.CASTLES_AT_BEGINNING;
|
||||
else {
|
||||
addTroops = Math.max(3, numRegions / GameConstants.TROOPS_PER_ROUND_DIVISOR);
|
||||
addScore(currentPlayer, addTroops * 5);
|
||||
|
||||
for(Kingdom kingdom : gameMap.getKingdoms()) {
|
||||
if(kingdom.getOwner() == currentPlayer) {
|
||||
addScore(currentPlayer, 10);
|
||||
addTroops++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currentPlayer.addTroops(addTroops);
|
||||
boolean isAI = (currentPlayer instanceof AI);
|
||||
gameInterface.onNextTurn(currentPlayer, addTroops, !isAI);
|
||||
if(isAI) {
|
||||
((AI)currentPlayer).doNextTurn(this);
|
||||
}
|
||||
|
||||
playerQueue.add(currentPlayer);
|
||||
}
|
||||
|
||||
public Player getCurrentPlayer() {
|
||||
return this.currentPlayer;
|
||||
}
|
||||
|
||||
public List<Player> getPlayers() {
|
||||
return this.players;
|
||||
}
|
||||
|
||||
public GameMap getMap() {
|
||||
return this.gameMap;
|
||||
}
|
||||
|
||||
public boolean isOver() {
|
||||
return this.isOver;
|
||||
}
|
||||
}
|
||||
41
Projektgruppe_175/src/game/GameConstants.java
Normal file
41
Projektgruppe_175/src/game/GameConstants.java
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package game;
|
||||
|
||||
import game.goals.*;
|
||||
import game.players.*;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public class GameConstants {
|
||||
|
||||
public static final int MAX_PLAYERS = 4;
|
||||
|
||||
// Determines how many regions are generated per player,
|
||||
// e.g. PlayerCount * 7 for Small, PlayerCount * 14 for Medium and PlayerCount * 21 for Large Maps
|
||||
public static final int CASTLES_NUMBER_MULTIPLIER = 7;
|
||||
public static final int CASTLES_AT_BEGINNING = 3;
|
||||
public static final int TROOPS_PER_ROUND_DIVISOR = 3;
|
||||
|
||||
public static final Color COLOR_WATER = Color.BLUE;
|
||||
public static final Color COLOR_SAND = new Color(210, 170, 109);
|
||||
public static final Color COLOR_GRASS = new Color(50, 89, 40);
|
||||
public static final Color COLOR_STONE = Color.GRAY;
|
||||
public static final Color COLOR_SNOW = Color.WHITE;
|
||||
|
||||
public static final Color PLAYER_COLORS[] = {
|
||||
Color.CYAN,
|
||||
Color.RED,
|
||||
Color.GREEN,
|
||||
Color.ORANGE
|
||||
};
|
||||
|
||||
public static final Goal GAME_GOALS[] = {
|
||||
new ConquerGoal(),
|
||||
// TODO: Add more Goals
|
||||
};
|
||||
|
||||
public static final Class<?> PLAYER_TYPES[] = {
|
||||
Human.class,
|
||||
BasicAI.class,
|
||||
// TODO: Add more Player types, like different AIs
|
||||
};
|
||||
}
|
||||
18
Projektgruppe_175/src/game/GameInterface.java
Normal file
18
Projektgruppe_175/src/game/GameInterface.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package game;
|
||||
|
||||
import game.map.Castle;
|
||||
|
||||
public interface GameInterface {
|
||||
|
||||
void onAttackStopped();
|
||||
void onAttackStarted(Castle source, Castle target, int troopCount);
|
||||
void onCastleChosen(Castle castle, Player player);
|
||||
void onNextTurn(Player currentPlayer, int troopsGot, boolean human);
|
||||
void onNewRound(int round);
|
||||
void onGameOver(Player winner);
|
||||
void onGameStarted(Game game);
|
||||
void onConquer(Castle castle, Player player);
|
||||
void onUpdate();
|
||||
void onAddScore(Player player, int score);
|
||||
int[] onRoll(Player player, int dices, boolean fastForward);
|
||||
}
|
||||
33
Projektgruppe_175/src/game/Goal.java
Normal file
33
Projektgruppe_175/src/game/Goal.java
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package game;
|
||||
|
||||
public abstract class Goal {
|
||||
|
||||
private Game game;
|
||||
private final String description;
|
||||
private final String name;
|
||||
|
||||
public Goal(String name, String description) {
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public void setGame(Game game) {
|
||||
this.game = game;
|
||||
}
|
||||
|
||||
public abstract boolean isCompleted();
|
||||
public abstract Player getWinner();
|
||||
public abstract boolean hasLost(Player player);
|
||||
|
||||
public final String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
protected Game getGame() {
|
||||
return this.game;
|
||||
}
|
||||
}
|
||||
88
Projektgruppe_175/src/game/Player.java
Normal file
88
Projektgruppe_175/src/game/Player.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package game;
|
||||
|
||||
import game.map.Castle;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Player {
|
||||
|
||||
private final String name;
|
||||
private Color color;
|
||||
private int points;
|
||||
private int remainingTroops;
|
||||
|
||||
protected Player(String name, Color color) {
|
||||
this.name = name;
|
||||
this.points = 0;
|
||||
this.color = color;
|
||||
this.remainingTroops = 0;
|
||||
}
|
||||
|
||||
public int getRemainingTroops() {
|
||||
return this.remainingTroops;
|
||||
}
|
||||
|
||||
public static Player createPlayer(Class<?> playerType, String name, Color color) {
|
||||
if(!Player.class.isAssignableFrom(playerType))
|
||||
throw new IllegalArgumentException("Not a player class");
|
||||
|
||||
try {
|
||||
Constructor<?> constructor = playerType.getConstructor(String.class, Color.class);
|
||||
return (Player) constructor.newInstance(name, color);
|
||||
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor(Color c) {
|
||||
this.color = c;
|
||||
}
|
||||
|
||||
public Color getColor() {
|
||||
return this.color;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getPoints() {
|
||||
return points;
|
||||
}
|
||||
|
||||
public void addPoints(int points) {
|
||||
this.points += points;
|
||||
}
|
||||
|
||||
public void addTroops(int troops) {
|
||||
if(troops < 0)
|
||||
return;
|
||||
|
||||
this.remainingTroops += troops;
|
||||
}
|
||||
|
||||
public void removeTroops(int troops) {
|
||||
if(this.remainingTroops - troops < 0 || troops < 0)
|
||||
return;
|
||||
|
||||
this.remainingTroops -= troops;
|
||||
}
|
||||
|
||||
public int getNumRegions(Game game) {
|
||||
return this.getCastles(game).size();
|
||||
}
|
||||
|
||||
public List<Castle> getCastles(Game game) {
|
||||
return game.getMap().getCastles().stream().filter(c -> c.getOwner() == this).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.remainingTroops = 0;
|
||||
this.points = 0;
|
||||
}
|
||||
}
|
||||
95
Projektgruppe_175/src/game/ScoreEntry.java
Normal file
95
Projektgruppe_175/src/game/ScoreEntry.java
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package game;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* Diese Klasse stellt einen Eintrag in der Bestenliste dar.
|
||||
* Sie enthält den Namen des Spielers, das Datum, die erreichte Punktzahl sowie den Spieltypen.
|
||||
*/
|
||||
public class ScoreEntry implements Comparable<ScoreEntry> {
|
||||
|
||||
private String name;
|
||||
private Date date;
|
||||
private int score;
|
||||
private String gameType;
|
||||
|
||||
/**
|
||||
* Erzeugt ein neues ScoreEntry-Objekt
|
||||
* @param name der Name des Spielers
|
||||
* @param score die erreichte Punktzahl
|
||||
* @param date das Datum
|
||||
* @param gameGoal der Spieltyp
|
||||
*/
|
||||
private ScoreEntry(String name, int score, Date date, String gameGoal) {
|
||||
this.name = name;
|
||||
this.score = score;
|
||||
this.date = date;
|
||||
this.gameType = gameGoal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erzeugt ein neues ScoreEntry-Objekt
|
||||
* @param player der Spieler
|
||||
* @param gameGoal der Spieltyp
|
||||
*/
|
||||
public ScoreEntry(Player player, Goal gameGoal) {
|
||||
this.name = player.getName();
|
||||
this.score = player.getPoints();
|
||||
this.date = new Date();
|
||||
this.gameType = gameGoal.getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(ScoreEntry scoreEntry) {
|
||||
return Integer.compare(this.score, scoreEntry.score);
|
||||
}
|
||||
|
||||
/**
|
||||
* Schreibt den Eintrag als neue Zeile mit dem gegebenen {@link PrintWriter}
|
||||
* Der Eintrag sollte im richtigen Format gespeichert werden.
|
||||
* @see #read(String)
|
||||
* @see Date#getTime()
|
||||
* @param printWriter der PrintWriter, mit dem der Eintrag geschrieben wird
|
||||
*/
|
||||
public void write(PrintWriter printWriter) {
|
||||
// TODO: ScoreEntry#write(PrintWriter)
|
||||
}
|
||||
|
||||
/**
|
||||
* List eine gegebene Zeile ein und wandelt dies in ein ScoreEntry-Objekt um.
|
||||
* Ist das Format der Zeile ungültig oder enthält es ungültige Daten, wird null zurückgegeben.
|
||||
* Eine gültige Zeile enthält in der Reihenfolge durch Semikolon getrennt:
|
||||
* den Namen, das Datum als Unix-Timestamp (in Millisekunden), die erreichte Punktzahl, den Spieltypen
|
||||
* Gültig wäre beispielsweise: "Florian;1546947397000;100;Eroberung"
|
||||
*
|
||||
*
|
||||
* @see String#split(String)
|
||||
* @see Long#parseLong(String)
|
||||
* @see Integer#parseInt(String)
|
||||
* @see Date#Date(long)
|
||||
*
|
||||
* @param line Die zu lesende Zeile
|
||||
* @return Ein ScoreEntry-Objekt oder null
|
||||
*/
|
||||
public static ScoreEntry read(String line) {
|
||||
// TODO: ScoreEntry#read(String)
|
||||
return null;
|
||||
}
|
||||
|
||||
public Date getDate() {
|
||||
return date;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public int getScore() {
|
||||
return this.score;
|
||||
}
|
||||
|
||||
public String getMode() {
|
||||
return this.gameType;
|
||||
}
|
||||
}
|
||||
45
Projektgruppe_175/src/game/goals/ConquerGoal.java
Normal file
45
Projektgruppe_175/src/game/goals/ConquerGoal.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package game.goals;
|
||||
|
||||
import game.Game;
|
||||
import game.Goal;
|
||||
import game.Player;
|
||||
import game.map.Castle;
|
||||
|
||||
public class ConquerGoal extends Goal {
|
||||
|
||||
public ConquerGoal() {
|
||||
super("Eroberung", "Derjenige Spieler gewinnt, der als erstes alle Gebiete erobert hat.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCompleted() {
|
||||
return this.getWinner() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Player getWinner() {
|
||||
Game game = this.getGame();
|
||||
if(game.getRound() < 2)
|
||||
return null;
|
||||
|
||||
Player p = null;
|
||||
for(Castle c : game.getMap().getCastles()) {
|
||||
if(c.getOwner() == null)
|
||||
return null;
|
||||
else if(p == null)
|
||||
p = c.getOwner();
|
||||
else if(p != c.getOwner())
|
||||
return null;
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLost(Player player) {
|
||||
if(getGame().getRound() < 2)
|
||||
return false;
|
||||
|
||||
return player.getNumRegions(getGame()) == 0;
|
||||
}
|
||||
}
|
||||
128
Projektgruppe_175/src/game/map/Castle.java
Normal file
128
Projektgruppe_175/src/game/map/Castle.java
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package game.map;
|
||||
|
||||
import game.Player;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* Diese Klasse representiert eine Burg.
|
||||
* Jede Burg hat Koordinaten auf der Karte und einen Namen.
|
||||
* Falls die Burg einen Besitzer hat, hat sie auch eine Anzahl von zugewiesenen Truppen.
|
||||
* Die Burg kann weiterhin Teil eines Königreichs sein.
|
||||
*/
|
||||
public class Castle {
|
||||
|
||||
private int troopCount;
|
||||
private Player owner;
|
||||
private Kingdom kingdom;
|
||||
private Point location;
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Eine neue Burg erstellen
|
||||
* @param location die Koordinaten der Burg
|
||||
* @param name der Name der Burg
|
||||
*/
|
||||
public Castle(Point location, String name) {
|
||||
this.location = location;
|
||||
this.troopCount = 0;
|
||||
this.owner = null;
|
||||
this.kingdom = null;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Player getOwner() {
|
||||
return this.owner;
|
||||
}
|
||||
|
||||
public Kingdom getKingdom() {
|
||||
return this.kingdom;
|
||||
}
|
||||
|
||||
public int getTroopCount() {
|
||||
return this.troopCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truppen von dieser Burg zur angegebenen Burg bewegen.
|
||||
* Dies funktioniert nur, wenn die Besitzer übereinstimmen und bei der aktuellen Burg mindestens eine Truppe übrig bleibt
|
||||
* @param target
|
||||
* @param troops
|
||||
*/
|
||||
public void moveTroops(Castle target, int troops) {
|
||||
|
||||
// Troops can only be moved to own regions
|
||||
if(target.owner != this.owner)
|
||||
return;
|
||||
|
||||
// At least one unit must remain in the source region
|
||||
if(this.troopCount - troops < 1)
|
||||
return;
|
||||
|
||||
this.troopCount -= troops;
|
||||
target.troopCount += troops;
|
||||
}
|
||||
|
||||
public Point getLocationOnMap() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die eukldische Distanz zu dem angegebenen Punkt
|
||||
* @param dest die Zielkoordinate
|
||||
* @return die euklidische Distanz
|
||||
*/
|
||||
public double distance(Point dest) {
|
||||
return Math.sqrt(Math.pow(this.location.x - dest.x, 2) + Math.pow(this.location.y - dest.y, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Berechnet die eukldische Distanz zu der angegebenen Burg
|
||||
* @param next die Zielburg
|
||||
* @return die euklidische Distanz
|
||||
* @see #distance(Point)
|
||||
*/
|
||||
public double distance(Castle next) {
|
||||
Point otherLocation = next.getLocationOnMap();
|
||||
return this.distance(otherLocation);
|
||||
}
|
||||
|
||||
public void setOwner(Player player) {
|
||||
this.owner = player;
|
||||
}
|
||||
|
||||
public void addTroops(int i) {
|
||||
if(i <= 0)
|
||||
return;
|
||||
|
||||
this.troopCount += i;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
public void removeTroops(int i) {
|
||||
this.troopCount = Math.max(0, this.troopCount - i);
|
||||
if(this.troopCount == 0)
|
||||
this.owner = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Burg-Typen zurück. Falls die Burg einem Königreich angehört, wird der Typ des Königreichs zurückgegeben, ansonsten 0
|
||||
* @return der Burg-Typ für die Anzeige
|
||||
*/
|
||||
public int getType() {
|
||||
return this.kingdom == null ? 0 : this.kingdom.getType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Die Burg einem Königreich zuordnen
|
||||
* @param kingdom Ein Königreich oder null
|
||||
*/
|
||||
public void setKingdom(Kingdom kingdom) {
|
||||
this.kingdom = kingdom;
|
||||
if(kingdom != null)
|
||||
kingdom.addCastle(this);
|
||||
}
|
||||
}
|
||||
40
Projektgruppe_175/src/game/map/Clustering.java
Normal file
40
Projektgruppe_175/src/game/map/Clustering.java
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package game.map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* Diese Klasse teilt Burgen in Königreiche auf
|
||||
*/
|
||||
public class Clustering {
|
||||
|
||||
private Random random;
|
||||
private final List<Castle> allCastles;
|
||||
private final int kingdomCount;
|
||||
|
||||
/**
|
||||
* Ein neues Clustering-Objekt erzeugen.
|
||||
* @param castles Die Liste von Burgen, die aufgeteilt werden sollen
|
||||
* @param kingdomCount Die Anzahl von Königreichen die generiert werden sollen
|
||||
*/
|
||||
public Clustering(List<Castle> castles, int kingdomCount) {
|
||||
if (kingdomCount < 2)
|
||||
throw new IllegalArgumentException("Ungültige Anzahl an Königreichen");
|
||||
|
||||
this.random = new Random();
|
||||
this.kingdomCount = kingdomCount;
|
||||
this.allCastles = Collections.unmodifiableList(castles);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt eine Liste von Königreichen zurück.
|
||||
* Jedes Königreich sollte dabei einen Index im Bereich 0-5 bekommen, damit die Burg richtig angezeigt werden kann.
|
||||
* Siehe auch {@link Kingdom#getType()}
|
||||
*/
|
||||
public List<Kingdom> getPointsClusters() {
|
||||
// TODO Clustering#getPointsClusters()
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
256
Projektgruppe_175/src/game/map/GameMap.java
Normal file
256
Projektgruppe_175/src/game/map/GameMap.java
Normal file
|
|
@ -0,0 +1,256 @@
|
|||
package game.map;
|
||||
|
||||
import base.*;
|
||||
import game.GameConstants;
|
||||
import gui.Resources;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Diese Klasse representiert das Spielfeld. Sie beinhaltet das Hintergrundbild, welches mit Perlin noise erzeugt wurde,
|
||||
* eine Liste mit Königreichen und alle Burgen und deren Verbindungen als Graphen.
|
||||
*
|
||||
* Die Karte wird in mehreren Schritten generiert, siehe dazu {@link #generateRandomMap(int, int, int, int, int)}
|
||||
*/
|
||||
public class GameMap {
|
||||
|
||||
private BufferedImage backgroundImage;
|
||||
private Graph<Castle> castleGraph;
|
||||
private List<Kingdom> kingdoms;
|
||||
|
||||
// Map Generation
|
||||
private double[][] noiseValues;
|
||||
private int width, height, scale;
|
||||
|
||||
/**
|
||||
* Erzeugt eine neue leere Karte. Der Konstruktor sollte niemals direkt aufgerufen werden.
|
||||
* Um eine neue Karte zu erstellen, muss {@link #generateRandomMap(int, int, int, int, int)} verwendet werden
|
||||
* @param width die Breite der Karte
|
||||
* @param height die Höhe der Karte
|
||||
* @param scale der Skalierungsfaktor
|
||||
*/
|
||||
private GameMap(int width, int height, int scale) {
|
||||
this.castleGraph = new Graph<>();
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.scale = scale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wandelt einen Noise-Wert in eine Farbe um. Die Methode kann nach belieben angepasst werden
|
||||
* @param value der Perlin-Noise-Wert
|
||||
* @return die resultierende Farbe
|
||||
*/
|
||||
private Color doubleToColor(double value) {
|
||||
if (value <= 0.40)
|
||||
return GameConstants.COLOR_WATER;
|
||||
else if (value <= 0.5)
|
||||
return GameConstants.COLOR_SAND;
|
||||
else if (value <= 0.7)
|
||||
return GameConstants.COLOR_GRASS;
|
||||
else if (value <= 0.8)
|
||||
return GameConstants.COLOR_STONE;
|
||||
else
|
||||
return GameConstants.COLOR_SNOW;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hier wird das Hintergrund-Bild mittels Perlin-Noise erzeugt.
|
||||
* Siehe auch: {@link PerlinNoise}
|
||||
*/
|
||||
private void generateBackground() {
|
||||
PerlinNoise perlinNoise = new PerlinNoise(width, height, scale);
|
||||
Dimension realSize = perlinNoise.getRealSize();
|
||||
|
||||
noiseValues = new double[realSize.width][realSize.height];
|
||||
backgroundImage = new BufferedImage(realSize.width, realSize.height, BufferedImage.TYPE_INT_RGB);
|
||||
for (int x = 0; x < realSize.width; x++) {
|
||||
for (int y = 0; y < realSize.height; y++) {
|
||||
double noiseValue = perlinNoise.getNoise(x, y);
|
||||
noiseValues[x][y] = noiseValue;
|
||||
backgroundImage.setRGB(x, y, doubleToColor(noiseValue).getRGB());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hier werden die Burgen erzeugt.
|
||||
* Dabei wir die Karte in Felder unterteilt, sodass auf jedes Fals maximal eine Burg kommt.
|
||||
* Sollte auf einem Feld keine Position für eine Burg existieren (z.B. aufgrund von Wasser oder angrenzenden Burgen), wird dieses übersprungen.
|
||||
* Dadurch kann es vorkommen, dass nicht alle Burgen generiert werden
|
||||
* @param castleCount die maximale Anzahl der zu generierenden Burgen
|
||||
*/
|
||||
private void generateCastles(int castleCount) {
|
||||
double square = Math.ceil(Math.sqrt(castleCount));
|
||||
double length = width + height;
|
||||
|
||||
int tilesX = (int) Math.max(1, (width / length + 0.5) * square) + 5;
|
||||
int tilesY = (int) Math.max(1, (height / length + 0.5) * square) + 5;
|
||||
int tileW = (width * scale / tilesX);
|
||||
int tileH = (height * scale / tilesY);
|
||||
|
||||
if (tilesX * tilesY < castleCount) {
|
||||
throw new IllegalArgumentException(String.format("CALCULATION Error: tilesX=%d * tilesY=%d < castles=%d", tilesX, tilesY, castleCount));
|
||||
}
|
||||
|
||||
// Add possible tiles
|
||||
List<Point> possibleFields = new ArrayList<>(tilesX * tilesY);
|
||||
for (int x = 0; x < tilesX - 1; x++) {
|
||||
for (int y = 0; y < tilesY - 1; y++) {
|
||||
possibleFields.add(new Point(x, y));
|
||||
}
|
||||
}
|
||||
|
||||
// Generate castles
|
||||
List<String> possibleNames = generateCastleNames();
|
||||
int castlesGenerated = 0;
|
||||
while (possibleFields.size() > 0 && castlesGenerated < castleCount) {
|
||||
Point randomField = possibleFields.remove((int) (Math.random() * possibleFields.size()));
|
||||
int x0 = (int) ((randomField.x + 0.5) * tileW);
|
||||
int y0 = (int) ((randomField.y + 0.5) * tileH);
|
||||
|
||||
for (int x = (int) (0.5 * tileW); x >= 0; x--) {
|
||||
boolean positionFound = false;
|
||||
for (int y = (int) (0.5 * tileH); y >= 0; y--) {
|
||||
int x_mid = (int) (x0 + x + 0.5 * tileW);
|
||||
int y_mid = (int) (y0 + y + 0.5 * tileH);
|
||||
if (noiseValues[x_mid][y_mid] >= 0.6) {
|
||||
String name = possibleNames.isEmpty() ? "Burg " + (castlesGenerated + 1) :
|
||||
possibleNames.get((int) (Math.random() * possibleNames.size()));
|
||||
Castle newCastle = new Castle(new Point(x0 + x, y0 + y), name);
|
||||
boolean doesIntersect = false;
|
||||
|
||||
for (Castle r : castleGraph.getAllValues()) {
|
||||
if (r.distance(newCastle) < Math.max(tileW, tileH)) {
|
||||
doesIntersect = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!doesIntersect) {
|
||||
possibleNames.remove(name);
|
||||
castleGraph.addNode(newCastle);
|
||||
castlesGenerated++;
|
||||
positionFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (positionFound)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hier werden die Kanten erzeugt. Dazu werden zunächst alle Burgen durch eine Linie verbunden und anschließend
|
||||
* jede Burg mit allen anderen in einem bestimmten Radius nochmals verbunden
|
||||
*/
|
||||
private void generateEdges() {
|
||||
// TODO: GameMap#generateEdges()
|
||||
}
|
||||
|
||||
/**
|
||||
* Hier werden die Burgen in Königreiche unterteilt. Dazu wird der {@link Clustering} Algorithmus aufgerufen.
|
||||
* @param kingdomCount die Anzahl der zu generierenden Königreiche
|
||||
*/
|
||||
private void generateKingdoms(int kingdomCount) {
|
||||
if(kingdomCount > 0 && kingdomCount < castleGraph.getAllValues().size()) {
|
||||
Clustering clustering = new Clustering(castleGraph.getAllValues(), kingdomCount);
|
||||
kingdoms = clustering.getPointsClusters();
|
||||
} else {
|
||||
kingdoms = new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine neue Spielfeldkarte generieren.
|
||||
* Dazu werden folgende Schritte abgearbeitet:
|
||||
* 1. Das Hintergrundbild generieren
|
||||
* 2. Burgen generieren
|
||||
* 3. Kanten hinzufügen
|
||||
* 4. Burgen in Köngireiche unterteilen
|
||||
* @param width die Breite des Spielfelds
|
||||
* @param height die Höhe des Spielfelds
|
||||
* @param scale die Skalierung
|
||||
* @param castleCount die maximale Anzahl an Burgen
|
||||
* @param kingdomCount die Anzahl der Königreiche
|
||||
* @return eine neue GameMap-Instanz
|
||||
*/
|
||||
public static GameMap generateRandomMap(int width, int height, int scale, int castleCount, int kingdomCount) {
|
||||
|
||||
width = Math.max(width, 15);
|
||||
height = Math.max(height, 10);
|
||||
|
||||
if (scale <= 0 || castleCount <= 0)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
System.out.println(String.format("Generating new map, castles=%d, width=%d, height=%d, kingdoms=%d", castleCount, width, height, kingdomCount));
|
||||
GameMap gameMap = new GameMap(width, height, scale);
|
||||
gameMap.generateBackground();
|
||||
gameMap.generateCastles(castleCount);
|
||||
gameMap.generateEdges();
|
||||
gameMap.generateKingdoms(kingdomCount);
|
||||
|
||||
if(!gameMap.getGraph().allNodesConnected()) {
|
||||
System.out.println("Fehler bei der Verifikation: Es sind nicht alle Knoten miteinander verbunden!");
|
||||
return null;
|
||||
}
|
||||
|
||||
return gameMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generiert eine Liste von Zufallsnamen für Burgen. Dabei wird ein Prefix (Schloss, Burg oder Festung) an einen
|
||||
* vorhandenen Namen aus den Resourcen angefügt. Siehe auch: {@link Resources#getcastleNames()}
|
||||
* @return eine Liste mit Zufallsnamen
|
||||
*/
|
||||
private List<String> generateCastleNames() {
|
||||
String[] prefixes = {"Schloss", "Burg", "Festung"};
|
||||
List<String> names = Resources.getInstance().getCastleNames();
|
||||
List<String> nameList = new ArrayList<>(names.size());
|
||||
|
||||
for (String name : names) {
|
||||
String prefix = prefixes[(int) (Math.random() * prefixes.length)];
|
||||
nameList.add(prefix + " " + name);
|
||||
}
|
||||
|
||||
return nameList;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return this.backgroundImage.getWidth();
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return this.backgroundImage.getHeight();
|
||||
}
|
||||
|
||||
public BufferedImage getBackgroundImage() {
|
||||
return this.backgroundImage;
|
||||
}
|
||||
|
||||
public Dimension getSize() {
|
||||
return new Dimension(this.getWidth(), this.getHeight());
|
||||
}
|
||||
|
||||
public List<Castle> getCastles() {
|
||||
return castleGraph.getAllValues();
|
||||
}
|
||||
|
||||
public Graph<Castle> getGraph() {
|
||||
return this.castleGraph;
|
||||
}
|
||||
|
||||
public List<Edge<Castle>> getEdges() {
|
||||
return this.castleGraph.getEdges();
|
||||
}
|
||||
|
||||
public List<Kingdom> getKingdoms() {
|
||||
return this.kingdoms;
|
||||
}
|
||||
}
|
||||
75
Projektgruppe_175/src/game/map/Kingdom.java
Normal file
75
Projektgruppe_175/src/game/map/Kingdom.java
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
package game.map;
|
||||
|
||||
import game.Player;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Diese Klasse representiert ein Königreich. Jedes Königreich hat eine Liste von Burgen sowie einen Index {@link #type} im Bereich von 0-5
|
||||
*
|
||||
*/
|
||||
public class Kingdom {
|
||||
|
||||
private List<Castle> castles;
|
||||
private int type;
|
||||
|
||||
/**
|
||||
* Erstellt ein neues Königreich
|
||||
* @param type der Typ des Königreichs (im Bereich 0-5)
|
||||
*/
|
||||
public Kingdom(int type) {
|
||||
this.castles = new LinkedList<>();
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Burg zum Königreich hinzufügen
|
||||
* @param castle die Burg, die hinzugefügt werden soll
|
||||
*/
|
||||
public void addCastle(Castle castle) {
|
||||
this.castles.add(castle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Typen des Königreichs zurück. Dies wird zur korrekten Anzeige benötigt
|
||||
* @return der Typ des Königreichs.
|
||||
*/
|
||||
public int getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eine Burg aus dem Königreich entfernen
|
||||
* @param castle die zu entfernende Burg
|
||||
*/
|
||||
public void removeCastle(Castle castle) {
|
||||
this.castles.remove(castle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den Spieler zurück, der alle Burgen in dem Köngreich besitzt.
|
||||
* Sollte es keinen Spieler geben, der alle Burgen besitzt, wird null zurückgegeben.
|
||||
* @return der Besitzer oder null
|
||||
*/
|
||||
public Player getOwner() {
|
||||
if(castles.isEmpty())
|
||||
return null;
|
||||
|
||||
Player owner = castles.get(0).getOwner();
|
||||
for(Castle castle : castles) {
|
||||
if(castle.getOwner() != owner)
|
||||
return null;
|
||||
}
|
||||
|
||||
return owner;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt alle Burgen zurück, die in diesem Königreich liegen
|
||||
* @return Liste von Burgen im Königreich
|
||||
*/
|
||||
public List<Castle> getCastles() {
|
||||
return this.castles;
|
||||
}
|
||||
}
|
||||
27
Projektgruppe_175/src/game/map/MapSize.java
Normal file
27
Projektgruppe_175/src/game/map/MapSize.java
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package game.map;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Vector;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public enum MapSize {
|
||||
|
||||
SMALL("Klein"),
|
||||
MEDIUM("Mittel"),
|
||||
LARGE("Groß");
|
||||
|
||||
private String label;
|
||||
MapSize(String lbl) {
|
||||
this.label = lbl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
public static Vector<String> getMapSizes() {
|
||||
return Arrays.stream(values()).map(MapSize::toString).collect(Collectors.toCollection(Vector::new));
|
||||
}
|
||||
|
||||
}
|
||||
58
Projektgruppe_175/src/game/map/PathFinding.java
Normal file
58
Projektgruppe_175/src/game/map/PathFinding.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package game.map;
|
||||
|
||||
import base.GraphAlgorithm;
|
||||
import base.Edge;
|
||||
import base.Graph;
|
||||
import game.Player;
|
||||
import game.map.Castle;
|
||||
import gui.components.MapPanel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PathFinding extends GraphAlgorithm<Castle> {
|
||||
|
||||
private MapPanel.Action action;
|
||||
private Player currentPlayer;
|
||||
|
||||
public PathFinding(Graph<Castle> graph, Castle sourceCastle, MapPanel.Action action, Player currentPlayer) {
|
||||
super(graph, graph.getNode(sourceCastle));
|
||||
this.action = action;
|
||||
this.currentPlayer = currentPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getValue(Edge<Castle> edge) {
|
||||
Castle castleA = edge.getNodeA().getValue();
|
||||
Castle castleB = edge.getNodeB().getValue();
|
||||
return castleA.distance(castleB);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isPassable(Edge<Castle> edge) {
|
||||
|
||||
Castle castleA = edge.getNodeA().getValue();
|
||||
Castle castleB = edge.getNodeB().getValue();
|
||||
|
||||
// One of the regions should belong to the current player
|
||||
if(castleA.getOwner() != currentPlayer && castleB.getOwner() != currentPlayer)
|
||||
return false;
|
||||
|
||||
if(action == MapPanel.Action.ATTACKING) {
|
||||
return castleA.getOwner() != null && castleB.getOwner() != null;
|
||||
} else if(action == MapPanel.Action.MOVING) {
|
||||
|
||||
// One of the regions may be empty
|
||||
if(castleA.getOwner() == null || castleB.getOwner() == null)
|
||||
return true;
|
||||
|
||||
// Else both regions should belong to the current player
|
||||
return castleA.getOwner() == castleB.getOwner() && castleA.getOwner() == currentPlayer;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public List<Edge<Castle>> getPath(Castle targetCastle) {
|
||||
return this.getPath(getGraph().getNode(targetCastle));
|
||||
}
|
||||
}
|
||||
103
Projektgruppe_175/src/game/players/BasicAI.java
Normal file
103
Projektgruppe_175/src/game/players/BasicAI.java
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
package game.players;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import base.Edge;
|
||||
import base.Graph;
|
||||
import base.Node;
|
||||
import game.AI;
|
||||
import game.Game;
|
||||
import game.map.Castle;
|
||||
import gui.AttackThread;
|
||||
|
||||
public class BasicAI extends AI {
|
||||
|
||||
public BasicAI(String name, Color color) {
|
||||
super(name, color);
|
||||
}
|
||||
|
||||
private Castle getCastleWithFewestTroops(List<Castle> castles) {
|
||||
Castle fewestTroops = castles.get(0);
|
||||
for(Castle castle : castles) {
|
||||
if(castle.getTroopCount() < fewestTroops.getTroopCount()) {
|
||||
fewestTroops = castle;
|
||||
}
|
||||
}
|
||||
|
||||
return fewestTroops;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void actions(Game game) throws InterruptedException {
|
||||
if(game.getRound() == 1) {
|
||||
List<Castle> availableCastles = game.getMap().getCastles().stream().filter(c -> c.getOwner() == null).collect(Collectors.toList());
|
||||
while(availableCastles.size() > 0 && getRemainingTroops() > 0) {
|
||||
|
||||
sleep(1000);
|
||||
|
||||
Castle randomCastle = availableCastles.remove(this.getRandom().nextInt(availableCastles.size()));
|
||||
game.chooseCastle(randomCastle, this);
|
||||
}
|
||||
} else {
|
||||
|
||||
// 1. Distribute remaining troops
|
||||
Graph<Castle> graph = game.getMap().getGraph();
|
||||
List<Castle> castleNearEnemy = new ArrayList<>();
|
||||
for(Castle castle : this.getCastles(game)) {
|
||||
Node<Castle> node = graph.getNode(castle);
|
||||
for(Edge<Castle> edge : graph.getEdges(node)) {
|
||||
Castle otherCastle = edge.getOtherNode(node).getValue();
|
||||
if(otherCastle.getOwner() != this) {
|
||||
castleNearEnemy.add(castle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while(this.getRemainingTroops() > 0) {
|
||||
Castle fewestTroops = getCastleWithFewestTroops(castleNearEnemy);
|
||||
sleep(500);
|
||||
game.addTroops(this, fewestTroops, 1);
|
||||
}
|
||||
|
||||
boolean attackWon;
|
||||
|
||||
do {
|
||||
// 2. Move troops from inside to border
|
||||
for (Castle castle : this.getCastles(game)) {
|
||||
if (!castleNearEnemy.contains(castle) && castle.getTroopCount() > 1) {
|
||||
Castle fewestTroops = getCastleWithFewestTroops(castleNearEnemy);
|
||||
game.moveTroops(castle, fewestTroops, castle.getTroopCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. attack!
|
||||
attackWon = false;
|
||||
for (Castle castle : castleNearEnemy) {
|
||||
if(castle.getTroopCount() < 2)
|
||||
continue;
|
||||
|
||||
Node<Castle> node = graph.getNode(castle);
|
||||
for (Edge<Castle> edge : graph.getEdges(node)) {
|
||||
Castle otherCastle = edge.getOtherNode(node).getValue();
|
||||
if (otherCastle.getOwner() != this && castle.getTroopCount() >= otherCastle.getTroopCount()) {
|
||||
AttackThread attackThread = game.startAttack(castle, otherCastle, castle.getTroopCount());
|
||||
if(fastForward)
|
||||
attackThread.fastForward();
|
||||
|
||||
attackThread.join();
|
||||
attackWon = attackThread.getWinner() == this;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(attackWon)
|
||||
break;
|
||||
}
|
||||
} while(attackWon);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Projektgruppe_175/src/game/players/Human.java
Normal file
11
Projektgruppe_175/src/game/players/Human.java
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package game.players;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import game.Player;
|
||||
|
||||
public class Human extends Player {
|
||||
public Human(String name, Color color) {
|
||||
super(name, color);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue