/** * Copyright (C) 2013 Gundog Studios LLC. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.godsandtowers; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.LinkedList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import com.godsandtowers.core.GameInfo; import com.godsandtowers.sprites.Player; import com.godsandtowers.sprites.Races; import com.godsandtowers.util.TDWPreferences; import com.gundogstudios.modules.DesktopAssets; import com.gundogstudios.modules.Modules; import com.gundogstudios.modules.SystemLogger; import com.gundogstudios.modules.basic.BasicPreferenceModule; import com.gundogstudios.modules.basic.EmptyProfilerModule; public class Balancer { private static final boolean SAME_RACES = false; private static final boolean DIFFERENT_LEVELS = false; private static final int NUM_THREADS = 10; private static final int STARTING_LEVEL = 10; private static final int RUNS_PER_LEVEL = 10; private static final int MAX_LEVEL = 100000; private static final String FILE_NAME = "output/results.csv"; private static final CustomMessageModule MESSAGE_MODULE = new CustomMessageModule(); private static final BasicPreferenceModule PREFERENCE_MODULE = new BasicPreferenceModule(); public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { long start = System.currentTimeMillis(); Modules.LOG = new SystemLogger(); Modules.MESSENGER = MESSAGE_MODULE; Modules.PREFERENCES = PREFERENCE_MODULE; Modules.PROFILER = new EmptyProfilerModule(); PREFERENCE_MODULE.put(TDWPreferences.GAME_ENGINE_SPEED, 3); Modules.ASSETS = new DesktopAssets(); ExecutorService threadPool = Executors.newFixedThreadPool(NUM_THREADS); BufferedWriter writer = new BufferedWriter(new FileWriter(FILE_NAME)); LinkedList<Future<GameInfo>> futures = submitFutures(threadPool); ArrayList<Result> results = generateResults(writer, futures); writeResults(writer, results); calculateEntropy(writer, results); writer.close(); threadPool.shutdown(); System.out.println("Took " + (System.currentTimeMillis() - start) + " ms to run"); } private static void writeTitle(BufferedWriter writer) throws IOException { String date = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format( new Date(System.currentTimeMillis())); writer.append(date + ","); for (int levelTwo = STARTING_LEVEL; levelTwo <= MAX_LEVEL; levelTwo *= 10) { for (int racesTwo : Races.ALL_RACES) { writer.append(Races.toString(racesTwo) + "_" + levelTwo + ","); } } writer.append("\n"); } private static LinkedList<Future<GameInfo>> submitFutures(ExecutorService threadPool) { LinkedList<Future<GameInfo>> futures = new LinkedList<Future<GameInfo>>(); for (int levelOne = STARTING_LEVEL; levelOne <= MAX_LEVEL; levelOne *= 10) { for (int racesOne : Races.ALL_RACES) { for (int levelTwo = STARTING_LEVEL; levelTwo <= MAX_LEVEL; levelTwo *= 10) { for (int racesTwo : Races.ALL_RACES) { if ((racesOne == racesTwo && !SAME_RACES) || (levelOne != levelTwo && !DIFFERENT_LEVELS)) continue; for (int i = 0; i < RUNS_PER_LEVEL; i++) { GameCallable callable = new GameCallable(MESSAGE_MODULE, racesOne, levelOne, racesTwo, levelTwo); Future<GameInfo> future = threadPool.submit(callable); futures.add(future); } } } } } System.out.println("Submitted " + futures.size() + " futures"); return futures; } private static ArrayList<Result> generateResults(BufferedWriter writer, LinkedList<Future<GameInfo>> futures) throws IOException, InterruptedException, ExecutionException { ArrayList<Result> results = new ArrayList<Result>(); writeTitle(writer); for (int levelOne = STARTING_LEVEL; levelOne <= MAX_LEVEL; levelOne *= 10) { for (int racesOne : Races.ALL_RACES) { writer.append(Races.toString(racesOne) + "_" + levelOne + ","); for (int levelTwo = STARTING_LEVEL; levelTwo <= MAX_LEVEL; levelTwo *= 10) { for (int racesTwo : Races.ALL_RACES) { if ((racesOne == racesTwo && !SAME_RACES) || (levelOne != levelTwo && !DIFFERENT_LEVELS)) { writer.append(","); continue; } Result result = new Result(racesOne, levelOne, racesTwo, levelTwo); for (int i = 0; i < RUNS_PER_LEVEL; i++) { Future<GameInfo> future = futures.removeFirst(); GameInfo gameInfo = future.get(); Player[] players = gameInfo.getPlayers(); if (players[0].getLife() > 0 && players[1].getLife() <= 0) { result.wins++; } else if (players[0].getLife() <= 0 && players[1].getLife() > 0) { result.losses++; } else { result.draws++; } } results.add(result); writer.append(result.wins + "-" + result.losses + "-" + result.draws + ","); // System.out.println(Races.toString(racesOne) + "_" + levelOne + " vs " // + Races.toString(racesTwo) + "_" + levelTwo + " results: " + result.wins + "-" // + result.losses + "-" + result.draws); } } writer.append("\n"); } } return results; } private static void writeResults(BufferedWriter writer, ArrayList<Result> results) throws IOException { writer.append("\n\n"); for (int races : Races.ALL_RACES) { for (int levelOne = STARTING_LEVEL; levelOne <= MAX_LEVEL; levelOne *= 10) { writer.append(Races.toString(races) + "_" + levelOne + ","); int wins = 0; int losses = 0; int draws = 0; for (Result result : results) { if (races == result.racesOne && levelOne == result.levelOne) { wins += result.wins; losses += result.losses; draws += result.draws; } } writer.append(wins + "-" + losses + "-" + draws + ","); // System.out.println(Races.toString(races) + "_" + levelOne + " vs " + levelTwo + " results: " + // wins // + "-" + losses + "-" + draws); writer.append("\n"); } } } private static void calculateEntropy(BufferedWriter writer, ArrayList<Result> results) throws IOException { float entropy = 0; for (int races : Races.ALL_RACES) { float wins = 0; float losses = 0; float draws = 0; for (Result result : results) { if (result.levelOne == result.levelTwo) { if (races == result.racesOne) { wins += result.wins; losses += result.losses; draws += result.draws; } else if (races == result.racesTwo) { wins += result.losses; losses += result.wins; draws += result.draws; } } } float winPercentage = (wins) / (wins + losses); System.out.println(Races.toString(races) + " - " + winPercentage + " " + wins + "-" + losses + "-" + draws); entropy += Math.pow(winPercentage - .5f, 2); } entropy = (float) Math.sqrt(entropy); System.out.println("Total entropy for the game: " + entropy); } }