diff --git a/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java b/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java index 43167c9..204b666 100644 --- a/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java +++ b/framework/src/main/java/org/toop/framework/game/players/ArtificialPlayer.java @@ -14,7 +14,9 @@ import org.toop.framework.gameFramework.model.game.TurnBasedGame; */ public class ArtificialPlayer extends AbstractPlayer { - /** The AI instance used to calculate moves. */ + /** + * The AI instance used to calculate moves. + */ private final AI ai; /** diff --git a/game/src/test/java/research/AIData.java b/game/src/test/java/research/AIData.java new file mode 100644 index 0000000..3b44dba --- /dev/null +++ b/game/src/test/java/research/AIData.java @@ -0,0 +1,77 @@ +package research; + +public class AIData { + public String AI; + public long gamesPlayed; + public double winrate; + public double averageIterations; + public double averageIterations10; + public double averageIterations20; + public double averageIterations30; + + public AIData(String AI, long gamesPlayed, double winrate, double averageIterations, double averageIterations10, double averageIterations20, double averageIterations30) { + this.AI = AI; + this.gamesPlayed = gamesPlayed; + this.winrate = winrate; + this.averageIterations = averageIterations; + this.averageIterations10 = averageIterations10; + this.averageIterations20 = averageIterations20; + this.averageIterations30 = averageIterations30; + } + + public String getAI() { + return AI; + } + + public void setAI(String AI) { + this.AI = AI; + } + + public long getGamesPlayed() { + return gamesPlayed; + } + + public void setGamesPlayed(long gamesPlayed) { + this.gamesPlayed = gamesPlayed; + } + + public double getWinrate() { + return winrate; + } + + public void setWinrate(double winrate) { + this.winrate = winrate; + } + + public double getAverageIterations() { + return averageIterations; + } + + public void setAverageIterations(double averageIterations) { + this.averageIterations = averageIterations; + } + + public double getAverageIterations10() { + return averageIterations10; + } + + public void setAverageIterations10(double averageIterations10) { + this.averageIterations10 = averageIterations10; + } + + public double getAverageIterations20() { + return averageIterations20; + } + + public void setAverageIterations20(double averageIterations20) { + this.averageIterations20 = averageIterations20; + } + + public double getAverageIterations30() { + return averageIterations30; + } + + public void setAverageIterations30(double averageIterations30) { + this.averageIterations30 = averageIterations30; + } +} diff --git a/game/src/test/java/research/AITest.java b/game/src/test/java/research/AITest.java index e591048..42ee3ce 100644 --- a/game/src/test/java/research/AITest.java +++ b/game/src/test/java/research/AITest.java @@ -7,6 +7,8 @@ import org.toop.framework.game.players.ArtificialPlayer; import org.toop.game.players.ai.MCTSAI; import org.toop.game.players.ai.RandomAI; import org.toop.game.players.ai.mcts.MCTSAI1; +import org.toop.game.players.ai.mcts.MCTSAI2; +import org.toop.game.players.ai.mcts.MCTSAI3; import org.toop.game.players.ai.mcts.MCTSAI4; import java.io.BufferedWriter; @@ -17,270 +19,429 @@ import java.util.List; public class AITest { - private static int games = 2; + private static List matchupList = new ArrayList(); + private static List dataList = new ArrayList(); @BeforeAll - public static void setUp() { - var versions = new ArtificialPlayer[5]; - versions[0] = new ArtificialPlayer(new RandomAI(), "Random AI"); - versions[1] = new ArtificialPlayer(new MCTSAI1(20), "MCTS V1 AI"); - versions[2] = new ArtificialPlayer(new org.toop.game.players.ai.mcts.MCTSAI2(20), "MCTS V2 AI"); - versions[3] = new ArtificialPlayer(new org.toop.game.players.ai.mcts.MCTSAI3(20, 10), "MCTS V3 AI"); - versions[4] = new ArtificialPlayer(new MCTSAI4(20, 10), "MCTS V4 AI"); + public static void init() { + var versions = new ArtificialPlayer[4]; + versions[0] = new ArtificialPlayer(new MCTSAI1(100), "MCTS V1"); + versions[1] = new ArtificialPlayer(new MCTSAI2(100), "MCTS V2"); + versions[2] = new ArtificialPlayer(new MCTSAI3(100, 8), "MCTS V3"); + versions[3] = new ArtificialPlayer(new MCTSAI4(100, 8), "MCTS V4"); for (int i = 0; i < versions.length; i++) { for (int j = i + 1; j < versions.length; j++) { final int playerIndex1 = i % versions.length; final int playerIndex2 = j % versions.length; - addMatchup(versions[playerIndex1], versions[playerIndex2]); + addMatch(versions[playerIndex1], versions[playerIndex2]); + addMatch(versions[playerIndex2], versions[playerIndex1]); // home vs away system } } - } - @BeforeEach - public void setUpEach() { - matchupList = new ArrayList<>(); + public static void addMatch(ArtificialPlayer v1, ArtificialPlayer v2) { + matchupList.add(new Matchup(v1, v2)); + } + + public void addData(AIData data) { + dataList.add(data); } @Test - public void testIterationsInRealGame() { - for (int i = 0; i < matchups.size(); i++) { - testAIVSAI(games, getMatchup(i)); + public void testAIvsAI() { + for (Matchup m : matchupList) { + playGame(m); } } - - private void testAIVSAI(int games, ArtificialPlayer[] ais) { - - List> gamesList = new ArrayList<>(); - for (int i = 0; i < games; i++) { - final BitboardReversi match = new BitboardReversi(); - match.init(ais); - - List iterations1 = new ArrayList<>(); - List iterations2 = new ArrayList<>(); - - while (!match.isTerminal()) { - final int currentAI = match.getCurrentTurn(); - final long move = ais[currentAI].getMove(match); - if (ais[currentAI].getAi() instanceof MCTSAI) { - final int lastIterations = ((MCTSAI) ais[currentAI].getAi()).getLastIterations(); - if (currentAI == 0) { - iterations1.add(lastIterations); - } - else if (currentAI == 1){ - iterations2.add(lastIterations); - } + public void playGame(Matchup m) { + List iterationsAI1 = new ArrayList<>(); + List iterationsAI2 = new ArrayList<>(); + final BitboardReversi match = new BitboardReversi(); + ArtificialPlayer[] players = new ArtificialPlayer[2]; + players[0] = m.getPlayer1(); + players[1] = m.getPlayer2(); + match.init(players); + while (!match.isTerminal()) { + final int currentAI = match.getCurrentTurn(); + final long move = players[currentAI].getMove(match); + if (players[currentAI].getAi() instanceof MCTSAI) { + final int lastIterations = ((MCTSAI) players[currentAI].getAi()).getLastIterations(); + if (currentAI == 0) { + iterationsAI1.add(lastIterations); + } else { + iterationsAI2.add(lastIterations); } - match.play(move); } - int winner = match.getWinner(); - iterations1.addFirst(winner); - iterations1.add(-999); //separator - iterations1.addAll(iterations2); - - gamesList.add(iterations1); + match.play(move); } - matchupList.add(gamesList); + generateData(m, match, iterationsAI1, iterationsAI2); } - @Test - public void testIterationsAtFixedMove() - { - for (ArtificialPlayer[] matchup : matchups) { - List> gamesList = new ArrayList<>(); - for (int j = 0; j < games; j++) { - final BitboardReversi match = new BitboardReversi(); - match.init(matchup); - - List iterations = new ArrayList<>(); - - for (Long move : fixedMoveSet) { - match.play(move); - if (move == 32L) { - break; - } - } - iterations.add(-999); - var player = matchup[match.getCurrentTurn()]; - for (int k = 0; k < 10; k++) { - player.getMove(match); - if (player.getAi() instanceof MCTSAI) { - iterations.add(((MCTSAI) player.getAi()).getLastIterations()); - } - } - gamesList.add(iterations); + public void generateData(Matchup matchup, BitboardReversi match, List iterationsAI1, List iterationsAI2) { + boolean matchup1Found = false; + boolean matchup2Found = false; + for (AIData aiData : dataList) { + if (aiData.getAI().equals(matchup.getPlayer1().getName())) { + matchup1Found = true; + } if (aiData.getAI().equals(matchup.getPlayer2().getName())) { + matchup2Found = true; } - matchupList.add(gamesList); } - } + if (!(matchup1Found)) { + addData(new AIData(matchup.getPlayer1().getName(), 0, 0, 0, 0, 0, 0)); + } + if (!(matchup2Found)) { + addData(new AIData(matchup.getPlayer2().getName(), 0, 0, 0, 0, 0, 0)); + } - - @Test - public void testIterationsInFixedGame(){ - for (ArtificialPlayer[] matchup : matchups) { - List> gamesList = new ArrayList<>(); - for (int j = 0; j < games; j++) { - final BitboardReversi match = new BitboardReversi(); - match.init(matchup); - - List iterations = new ArrayList<>(); - - iterations.add(-999); - - for (Long move : fixedMoveSet) { - var player = matchup[match.getCurrentTurn()]; - player.getMove(match); - if (player.getAi() instanceof MCTSAI) { - iterations.add(((MCTSAI) player.getAi()).getLastIterations()); - } - match.play(move); - } - - gamesList.add(iterations); + for (AIData aiData : dataList) { // set data for player 1 + if (aiData.getAI().equals(matchup.getPlayer1().getName())) { + aiData.setGamesPlayed(aiData.getGamesPlayed() + 1); + aiData.setWinrate(calculateWinrate(0, aiData.getWinrate(), aiData.getGamesPlayed(), match.getWinner())); + aiData.setAverageIterations(calculateAverageIterations(aiData.getAverageIterations(), iterationsAI1)); + aiData.setAverageIterations10(calculateAverageIterationsStartEnd(0, 10, aiData.getAverageIterations10(), iterationsAI1)); + aiData.setAverageIterations20(calculateAverageIterationsStartEnd(10, 20, aiData.getAverageIterations20(), iterationsAI1)); + aiData.setAverageIterations30(calculateAverageIterationsStartEnd(20, iterationsAI1.size(), aiData.getAverageIterations30(), iterationsAI1)); + } + } + + for (AIData aiData : dataList) { + if (aiData.getAI().equals(matchup.getPlayer2().getName())) { + aiData.setGamesPlayed(aiData.getGamesPlayed() + 1); + aiData.setWinrate(calculateWinrate(1, aiData.getWinrate(), aiData.getGamesPlayed(), match.getWinner())); + aiData.setAverageIterations(calculateAverageIterations(aiData.getAverageIterations(), iterationsAI2)); + aiData.setAverageIterations10(calculateAverageIterationsStartEnd(0, 10, aiData.getAverageIterations10(), iterationsAI2)); + aiData.setAverageIterations20(calculateAverageIterationsStartEnd(10, 20, aiData.getAverageIterations20(), iterationsAI2)); + aiData.setAverageIterations30(calculateAverageIterationsStartEnd(20, iterationsAI2.size(), aiData.getAverageIterations30(), iterationsAI2)); } - matchupList.add(gamesList); } } - @AfterEach - public void tearDown() { - data.add(matchupList); + public double calculateWinrate(int player, double winrate, long gamesPlayed, int winner) { + double result; + if (winner == 0 && player == 0 || winner == 1 && player == 1) { + return (winrate * (gamesPlayed - 1) + 1) / gamesPlayed; + } else if (winner == 0 && player == 1 || winner == 1 && player == 0) { + return (winrate * (gamesPlayed - 1) + 0) / gamesPlayed; + } + return (winrate * (gamesPlayed - 1) + 0) / gamesPlayed; + } + + public double calculateAverageIterations(double averageIterations, List thisGameIterations) { + double thisGameIterationsAverage = 0; + for (int iterations = 0; iterations < thisGameIterations.size(); iterations += 1) { + thisGameIterationsAverage += thisGameIterations.get(iterations); + } + thisGameIterationsAverage /= thisGameIterations.size(); + return (averageIterations + thisGameIterationsAverage) / 2; + } + + public double calculateAverageIterationsStartEnd(int start, int end, double averageIterations, List thisGameIterations) { + double thisGameIterationsAverage = 0; + for (int iterations = start; iterations < end; iterations += 1) { + thisGameIterationsAverage += thisGameIterations.get(iterations); + } + thisGameIterationsAverage /= (end - start); + return (averageIterations + thisGameIterationsAverage) / 2; } @AfterAll - public static void writeAfterTests(){ + public static void writeAfterTests() { try { - writeToCsv("Data.csv", data); - }catch (IOException e) { - + writeToCsv("Data.csv", dataList); + } catch (IOException e) { + e.printStackTrace(); } } - - public static void writeToCsv(String filepath, List>>> data) throws IOException { + public static void writeToCsv(String filepath, List dataList) throws IOException { try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) { - - writer.write("TestID,Matchup,GameNr,Winner"); - for (int i = 0; i < data.size(); i++) { - writer.write(",Iterations"); - } - + writer.write("AI Name,Games Played,Winrate,Average Iterations,Average Iterations 0-10, Average Iterations 11-20, Average Iterations 20-30"); writer.newLine(); - - for (int TestID = 0; TestID < data.size(); TestID++) { - List>> testCase = data.get(TestID); - - for (int matchupNr = 0; matchupNr < testCase.size(); matchupNr++) { - List> matchup = testCase.get(matchupNr); - - for (int gameNr = 0; gameNr < matchup.size(); gameNr++) { - List game = matchup.get(gameNr); - writer.write((TestID + 1) + "," + (getMatchupName(matchupNr)) + "," + (gameNr + 1)); - for (int i = 0; i < game.size(); i++) { - if (i == 0) { - writer.write("," + getWinnerFromMatchup(game.get(i),matchupNr)); - } else { - writer.write("," + game.get(i)); - } - } - writer.newLine(); - } - } + for (AIData data : dataList) { + writer.write( + data.getAI() + "," + + data.getGamesPlayed() + "," + + data.getWinrate() + "," + + Math.round(data.getAverageIterations()) + "," + + Math.round(data.getAverageIterations10()) + "," + + Math.round(data.getAverageIterations20()) + "," + + Math.round(data.getAverageIterations30())); + writer.newLine(); } } - } - - - private static final List>>> data = new ArrayList<>(); - private List>> matchupList = new ArrayList<>(); - private static final List matchupNames = new ArrayList<>(); - private static final List matchups = new ArrayList<>(); - private static String getMatchupName(int matchupNr){ - return matchupNames.get(matchupNr); - } - private static ArtificialPlayer[] getMatchup(int matchupNr){ - return matchups.get(matchupNr); - } - private static String getWinnerFromMatchup(Integer winner, int matchupNr){ - String matchup = matchupNames.get(matchupNr); - - String[] parts = matchup.split(" vs "); - - if (parts.length != 2) { - return "Invalid matchup formatting."; - } - - return winner == 0 ? parts[0] : winner == 1? parts[1] : winner == -999? "NVT" : "Tie"; - } - - private static void addMatchup(ArtificialPlayer player1, ArtificialPlayer player2){ - matchups.add(new ArtificialPlayer[] {player1,player2}); - matchupNames.add(player1.getName() + " vs " + player2.getName()); - } - - private final Long[] fixedMoveSet = new Long[]{17592186044416L, - 35184372088832L, - 67108864L, - 8796093022208L, - 2251799813685248L, - 288230376151711744L, - 70368744177664L, - 1125899906842624L, - 137438953472L, - 140737488355328L, - 4503599627370496L, - 2305843009213693952L, - 18014398509481984L, - 274877906944L, - 576460752303423488L, - -9223372036854775808L, - 549755813888L, - 1152921504606846976L, - 144115188075855872L, - 72057594037927936L, - 36028797018963968L, - 17179869184L, - 2199023255552L, - 1048576L, - 4398046511104L, - 281474976710656L, - 9007199254740992L, - 2147483648L, - 1073741824L, - 33554432L, - 262144L, - 8388608L, - 8192L, - 4611686018427387904L, - 4294967296L, - 524288L, - 4096L, - 16777216L, - 65536L, - 32L, - 2048L, - 8L, - 4L, - 8589934592L, - 16L, - 2097152L, - 4194304L, - 1024L, - 512L, - 16384L, - 536870912L, - 1099511627776L, - 64L, - 562949953421312L, - 128L, - 1L, - 32768L, - 2L, - 256L, - 131072L}; - - } + +//public class AITest { + +// private static int games = 2; +// +// @BeforeAll +// public static void setUp() { +// var versions = new ArtificialPlayer[5]; +// versions[0] = new ArtificialPlayer(new RandomAI(), "Random AI"); +// versions[1] = new ArtificialPlayer(new MCTSAI1(20), "MCTS V1 AI"); +// versions[2] = new ArtificialPlayer(new org.toop.game.players.ai.mcts.MCTSAI2(20), "MCTS V2 AI"); +// versions[3] = new ArtificialPlayer(new org.toop.game.players.ai.mcts.MCTSAI3(20, 10), "MCTS V3 AI"); +// versions[4] = new ArtificialPlayer(new MCTSAI4(20, 10), "MCTS V4 AI"); +// +// for (int i = 0; i < versions.length; i++) { +// for (int j = i + 1; j < versions.length; j++) { +// final int playerIndex1 = i % versions.length; +// final int playerIndex2 = j % versions.length; +// addMatchup(versions[playerIndex1], versions[playerIndex2]); +// } +// } +// +// } +// +// @BeforeEach +// public void setUpEach() { +// matchupList = new ArrayList<>(); +// } +// +// @Test +// public void testIterationsInRealGame() { +// for (int i = 0; i < matchups.size(); i++) { +// testAIVSAI(games, getMatchup(i)); +// } +// } +// +// +// private void testAIVSAI(int games, ArtificialPlayer[] ais) { +// +// List> gamesList = new ArrayList<>(); +// for (int i = 0; i < games; i++) { +// final BitboardReversi match = new BitboardReversi(); +// match.init(ais); +// +// List iterations1 = new ArrayList<>(); +// List iterations2 = new ArrayList<>(); +// +// while (!match.isTerminal()) { +// final int currentAI = match.getCurrentTurn(); +// final long move = ais[currentAI].getMove(match); +// if (ais[currentAI].getAi() instanceof MCTSAI) { +// final int lastIterations = ((MCTSAI) ais[currentAI].getAi()).getLastIterations(); +// if (currentAI == 0) { +// iterations1.add(lastIterations); +// } else if (currentAI == 1) { +// iterations2.add(lastIterations); +// } +// } +// match.play(move); +// } +// int winner = match.getWinner(); +// iterations1.addFirst(winner); +//// iterations1.add(-999); +// iterations1.addAll(iterations2); +// +// gamesList.add(iterations1); +// } +// matchupList.add(gamesList); +// } +// +// @Test +// public void testIterationsAtFixedMove() { +// for (ArtificialPlayer[] matchup : matchups) { +// List> gamesList = new ArrayList<>(); +// for (int j = 0; j < games; j++) { +// final BitboardReversi match = new BitboardReversi(); +// match.init(matchup); +// +// List iterations = new ArrayList<>(); +// +// for (Long move : fixedMoveSet) { +// match.play(move); +// if (move == 32L) { +// break; +// } +// } +//// iterations.add(-999); +// var player = matchup[match.getCurrentTurn()]; +// for (int k = 0; k < 10; k++) { +// player.getMove(match); +// if (player.getAi() instanceof MCTSAI) { +// iterations.add(((MCTSAI) player.getAi()).getLastIterations()); +// } +// } +// gamesList.add(iterations); +// } +// matchupList.add(gamesList); +// } +// } +// +// +// @Test +// public void testIterationsInFixedGame() { +// for (ArtificialPlayer[] matchup : matchups) { +// List> gamesList = new ArrayList<>(); +// for (int j = 0; j < games; j++) { +// final BitboardReversi match = new BitboardReversi(); +// match.init(matchup); +// +// List iterations = new ArrayList<>(); +// +// iterations.add(-999); +// +// for (Long move : fixedMoveSet) { +// var player = matchup[match.getCurrentTurn()]; +// player.getMove(match); +// if (player.getAi() instanceof MCTSAI) { +// iterations.add(((MCTSAI) player.getAi()).getLastIterations()); +// } +// match.play(move); +// } +// +// gamesList.add(iterations); +// } +// matchupList.add(gamesList); +// } +// } +// +// @AfterEach +// public void tearDown() { +// data.add(matchupList); +// } +// +// @AfterAll +// public static void writeAfterTests() { +// try { +// writeToCsv("Data.csv", data); +// } catch (IOException e) { +// +// } +// } +// +// +// public static void writeToCsv(String filepath, List>>> data) throws IOException { +// try (BufferedWriter writer = new BufferedWriter(new FileWriter(filepath))) { +// +// writer.write("TestID,Matchup,GameNr,Winner"); +// for (int i = 0; i < data.size(); i++) { +// writer.write(",Iterations"); +// } +// +// writer.newLine(); +// +// for (int TestID = 0; TestID < data.size(); TestID++) { +// List>> testCase = data.get(TestID); +// +// for (int matchupNr = 0; matchupNr < testCase.size(); matchupNr++) { +// List> matchup = testCase.get(matchupNr); +// +// for (int gameNr = 0; gameNr < matchup.size(); gameNr++) { +// List game = matchup.get(gameNr); +// writer.write((TestID + 1) + "," + (getMatchupName(matchupNr)) + "," + (gameNr + 1)); +// for (int i = 0; i < game.size(); i++) { +// if (i == 0) { +// writer.write("," + getWinnerFromMatchup(game.get(i), matchupNr)); +// } else { +// writer.write("," + game.get(i)); +// } +// } +// writer.newLine(); +// } +// } +// } +// } +// +// } +// +// +// private static final List>>> data = new ArrayList<>(); +// private List>> matchupList = new ArrayList<>(); +// private static final List matchupNames = new ArrayList<>(); +// private static final List matchups = new ArrayList<>(); +// +// private static String getMatchupName(int matchupNr) { +// return matchupNames.get(matchupNr); +// } +// +// private static ArtificialPlayer[] getMatchup(int matchupNr) { +// return matchups.get(matchupNr); +// } +// +// private static String getWinnerFromMatchup(Integer winner, int matchupNr) { +// String matchup = matchupNames.get(matchupNr); +// +// String[] parts = matchup.split(" vs "); +// +// if (parts.length != 2) { +// return "Invalid matchup formatting."; +// } +// +// return winner == 0 ? parts[0] : winner == 1 ? parts[1] : winner == -999 ? "NVT" : "Tie"; +// } +// +// private static void addMatchup(ArtificialPlayer player1, ArtificialPlayer player2) { +// matchups.add(new ArtificialPlayer[]{player1, player2}); +// matchupNames.add(player1.getName() + " vs " + player2.getName()); +// } +//} + +// private final Long[] fixedMoveSet = new Long[]{17592186044416L, +// 35184372088832L, +// 67108864L, +// 8796093022208L, +// 2251799813685248L, +// 288230376151711744L, +// 70368744177664L, +// 1125899906842624L, +// 137438953472L, +// 140737488355328L, +// 4503599627370496L, +// 2305843009213693952L, +// 18014398509481984L, +// 274877906944L, +// 576460752303423488L, +// -9223372036854775808L, +// 549755813888L, +// 1152921504606846976L, +// 144115188075855872L, +// 72057594037927936L, +// 36028797018963968L, +// 17179869184L, +// 2199023255552L, +// 1048576L, +// 4398046511104L, +// 281474976710656L, +// 9007199254740992L, +// 2147483648L, +// 1073741824L, +// 33554432L, +// 262144L, +// 8388608L, +// 8192L, +// 4611686018427387904L, +// 4294967296L, +// 524288L, +// 4096L, +// 16777216L, +// 65536L, +// 32L, +// 2048L, +// 8L, +// 4L, +// 8589934592L, +// 16L, +// 2097152L, +// 4194304L, +// 1024L, +// 512L, +// 16384L, +// 536870912L, +// 1099511627776L, +// 64L, +// 562949953421312L, +// 128L, +// 1L, +// 32768L, +// 2L, +// 256L, +// 131072L}; +// } + diff --git a/game/src/test/java/research/Matchup.java b/game/src/test/java/research/Matchup.java new file mode 100644 index 0000000..8f93ff2 --- /dev/null +++ b/game/src/test/java/research/Matchup.java @@ -0,0 +1,30 @@ +package research; + +import org.toop.framework.game.players.ArtificialPlayer; + +import java.util.ArrayList; +import java.util.List; + +public class Matchup { + public ArtificialPlayer player1; + public ArtificialPlayer player2; + + public Matchup(ArtificialPlayer player1, ArtificialPlayer player2) { + this.player1 = player1; + this.player2 = player2; + } + + public Matchup() {} + + public String toString() { + return player1.toString() + " VS " + player2.toString(); + } + + public ArtificialPlayer getPlayer1() { + return player1; + } + + public ArtificialPlayer getPlayer2() { + return player2; + } +}