ar.edu.unrc.gametictactoe.performanceandtraining.configurations.StatisticExperiment.java Source code

Java tutorial

Introduction

Here is the source code for ar.edu.unrc.gametictactoe.performanceandtraining.configurations.StatisticExperiment.java

Source

/*
 * Copyright (C) 2016  Lucia Bressan <lucyluz333@gmial.com>,
 *                     Franco Pellegrini <francogpellegrini@gmail.com>,
 *                     Renzo Bianchini <renzobianchini85@gmail.com
 *
 *   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 ar.edu.unrc.gametictactoe.performanceandtraining.configurations;

import ar.edu.unrc.gametictactoe.GameTicTacToe;
import ar.edu.unrc.gametictactoe.PerceptronConfigurationTicTacToe;
import ar.edu.unrc.gametictactoe.Players;
import ar.edu.unrc.tdlearning.perceptron.interfaces.IPerceptronInterface;
import ar.edu.unrc.tdlearning.perceptron.learning.TDLambdaLearning;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import static java.lang.Math.round;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.IntStream;
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Font;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;
import static java.lang.Math.round;

/**
 *
 * @author lucia bressan, franco pellegrini, renzo bianchini
 * @param <NeuralNetworkClass>
 */
public abstract class StatisticExperiment<NeuralNetworkClass> {

    private Date dateForFileName;
    private SimpleDateFormat dateFormater;
    private double drawRate;

    private String experimentName;
    private String fileName;

    private int gamesToPlay;
    private TDLambdaLearning learningMethod;
    private int saveBackupEvery;
    private int simulations;
    private double winRatePlayer1;
    private double winRatePlayer2;

    /**
     *
     */
    protected LearningExperiment<NeuralNetworkClass> learningExperiment;

    /**
     *
     * @param learningExperiment
     */
    public StatisticExperiment(LearningExperiment<NeuralNetworkClass> learningExperiment) {
        this.learningExperiment = learningExperiment;
    }

    /**
     *
     * @param filePath
     * @param backupFiles
     * @param resultsPerFile
     * @param resultsRandom
     * @param randomPerceptronFile <p>
     * @throws IOException
     * @throws InvalidFormatException
     */
    public void exportToExcel(String filePath, List<File> backupFiles, Map<File, StatisticForCalc> resultsPerFile,
            Map<File, StatisticForCalc> resultsRandom, File randomPerceptronFile)
            throws IOException, InvalidFormatException {
        InputStream inputXLSX = this.getClass()
                .getResourceAsStream("/ar/edu/unrc/gametictactoe/resources/EstadisticasTicTacToe.xlsx");
        Workbook wb = WorkbookFactory.create(inputXLSX);

        try (FileOutputStream outputXLSX = new FileOutputStream(
                filePath + "_" + dateFormater.format(dateForFileName) + "_STATISTICS" + ".xlsx")) {
            //============= imptimimos en la hoja de % Of Games Won ===================
            Sheet sheet = wb.getSheetAt(0);
            //Estilo par los titulos de las tablas
            int rowStartTitle = 0;
            int colStartTitle = 2;
            int rowStart = 1;
            int colStart = 3;
            Row rowPlayer1;
            Row rowPlayer2;
            Row rowDraw;
            // Luego creamos el objeto que se encargar de aplicar el estilo a la celda
            Font fontCellTitle = wb.createFont();
            fontCellTitle.setFontHeightInPoints((short) 10);
            fontCellTitle.setFontName("Arial");
            fontCellTitle.setBoldweight(Font.BOLDWEIGHT_BOLD);
            CellStyle CellStyleTitle = wb.createCellStyle();
            CellStyleTitle.setWrapText(true);
            CellStyleTitle.setAlignment(CellStyle.ALIGN_CENTER);
            CellStyleTitle.setVerticalAlignment(CellStyle.VERTICAL_TOP);
            CellStyleTitle.setFont(fontCellTitle);

            // Establecemos el tipo de sombreado de nuestra celda
            CellStyleTitle.setFillBackgroundColor(IndexedColors.LIGHT_CORNFLOWER_BLUE.getIndex());
            CellStyleTitle.setFillPattern(CellStyle.SOLID_FOREGROUND);
            loadTitle(rowStartTitle, colStartTitle, sheet, backupFiles.size(), CellStyleTitle);
            //estilo titulo finalizado

            //Estilo de celdas con los valores de las estadisticas
            CellStyle cellStyle = wb.createCellStyle();
            cellStyle.setWrapText(true);
            /* We are now ready to set borders for this style */
            /* Draw a thin left border */
            cellStyle.setBorderLeft(CellStyle.BORDER_THIN);
            /* Add medium right border */
            cellStyle.setBorderRight(CellStyle.BORDER_THIN);
            /* Add dashed top border */
            cellStyle.setBorderTop(CellStyle.BORDER_THIN);
            /* Add dotted bottom border */
            cellStyle.setBorderBottom(CellStyle.BORDER_THIN);
            //estilo celdas finalizado
            //loadTitle(rowStartTitle, colStartTitle, sheet, backupFiles.size(), CellStyleTitle);
            rowPlayer1 = sheet.getRow(rowStart);
            rowPlayer2 = sheet.getRow(rowStart + 1);
            rowDraw = sheet.getRow(rowStart + 2);
            for (int file = 0; file < backupFiles.size(); file++) {
                Cell cellPlayer1 = rowPlayer1.createCell(file + colStart, Cell.CELL_TYPE_NUMERIC);
                Cell cellPlayer2 = rowPlayer2.createCell(file + colStart, Cell.CELL_TYPE_NUMERIC);
                Cell cellDraw = rowDraw.createCell(file + colStart, Cell.CELL_TYPE_NUMERIC);
                cellPlayer1.setCellStyle(cellStyle);
                cellPlayer2.setCellStyle(cellStyle);
                cellDraw.setCellStyle(cellStyle);
                Double cellValuePlayer1 = resultsPerFile.get(backupFiles.get(file)).getWinRatePlayer1();
                Double cellValuePlayer2 = resultsPerFile.get(backupFiles.get(file)).getWinRatePlayer2();
                Double cellValueDraw = resultsPerFile.get(backupFiles.get(file)).getDrawRate();
                assert cellValuePlayer1 <= 100 && cellValuePlayer1 >= 0;
                assert cellValuePlayer2 <= 100 && cellValuePlayer2 >= 0;
                assert cellValueDraw <= 100 && cellValueDraw >= 0;
                //assert cellValueDraw + cellValuePlayer1 + cellValuePlayer2 == 100;
                cellDraw.setCellValue(cellValueDraw);
                cellPlayer1.setCellValue(cellValuePlayer1);
                cellPlayer2.setCellValue(cellValuePlayer2);
            }
            if (!resultsRandom.isEmpty()) {
                int file = 0;//hay que ir a buscar el randomfile
                Cell cellDraw = rowDraw.createCell(file + colStart - 1, Cell.CELL_TYPE_NUMERIC);
                Cell cellPlayer1 = rowPlayer1.createCell(file + colStart - 1, Cell.CELL_TYPE_NUMERIC);
                Cell cellPlayer2 = rowPlayer2.createCell(file + colStart - 1, Cell.CELL_TYPE_NUMERIC);
                cellDraw.setCellStyle(cellStyle);
                cellPlayer1.setCellStyle(cellStyle);
                cellPlayer2.setCellStyle(cellStyle);
                //                StatisticForCalc get = resultsRandom.get(randomPerceptronFile);
                //                Double cellValuePlayer1 = get.getWinRatePlayer1();
                Double cellValuePlayer1 = resultsRandom.get(randomPerceptronFile).getWinRatePlayer1();
                Double cellValuePlayer2 = resultsRandom.get(randomPerceptronFile).getWinRatePlayer2();
                Double cellValueDraw = resultsRandom.get(randomPerceptronFile).getDrawRate();
                //assert cellValueDraw + cellValuePlayer1 + cellValuePlayer2 == 100;

                cellPlayer1.setCellValue(cellValuePlayer1);
                cellPlayer2.setCellValue(cellValuePlayer2);
                cellDraw.setCellValue(cellValueDraw);
            }
            wb.write(outputXLSX);
        }
    }

    /**
     * @param gamesToPlay the gamesToPlay to set
     */
    public void setGamesToPlayPerThread(int gamesToPlay) {
        this.gamesToPlay = gamesToPlay;
    }

    /**
     * @return the tileStatistics
     */
    public StatisticForCalc getStatistics() {
        StatisticForCalc statistic = new StatisticForCalc();
        statistic.setWinRatePlayer1(winRatePlayer1);
        statistic.setDrawRate(drawRate);
        statistic.setWinRatePlayer2(winRatePlayer2);
        return statistic;
    }

    /**
     *
     * @param rowStartTitle
     * @param colStartTitle
     * @param sheet
     * @param backupFilesSize
     * @param CellStyleTitle
     */
    public void loadTitle(int rowStartTitle, int colStartTitle, Sheet sheet, int backupFilesSize,
            CellStyle CellStyleTitle) {
        int total_juegos = saveBackupEvery;
        Row row1 = sheet.getRow(rowStartTitle);
        for (int file = 1; file <= backupFilesSize; file++) {
            Cell cell = row1.createCell(file + colStartTitle, Cell.CELL_TYPE_NUMERIC);
            cell.setCellStyle(CellStyleTitle);
            Integer value = total_juegos * file;
            String valueStr = value.toString();
            String cellV = valueStr;
            if (valueStr.length() > 3) {
                cellV = valueStr.substring(0, valueStr.length() - 3) + "K";
            }
            cell.setCellValue(cellV);
        }
    }

    /**
     *
     * @param fileToProcess
     * @param delayPerMove  <p>
     * @throws Exception
     */
    public void processFile(String fileToProcess, int delayPerMove) throws Exception {
        if (learningExperiment.getPlayerForStatistics() == null
                || (learningExperiment.getPlayerForStatistics() != Players.PLAYER1
                        && learningExperiment.getPlayerForStatistics() != Players.PLAYER2)) {
            throw new IllegalStateException(
                    "La variable playerForStatistics debe ser Players.PLAYER1 o Players.PLAYER2");
        }

        //preparamos los destinos de las simulaciones para posterior sumatoria final
        List<ThreadResult> results = new ArrayList(simulations);
        List<GameTicTacToe<NeuralNetworkClass>> games = new ArrayList(simulations);
        List<INeuralNetworkInterfaceForTicTacToe<NeuralNetworkClass>> neuralNetworkInterfaces = new ArrayList(
                simulations);
        List<TDLambdaLearning> tdLambdaLearning = new ArrayList(simulations);

        for (int i = 0; i < simulations; i++) {
            PerceptronConfigurationTicTacToe<NeuralNetworkClass> tempPerceptronConfiguration = null;
            INeuralNetworkInterfaceForTicTacToe<NeuralNetworkClass> neuralNetworkInterfaceClone = null;
            IPerceptronInterface tempPerceptronInterface = null;
            if (!this.learningExperiment.isPlayer1Random() || !this.learningExperiment.isPlayer2Random()) {
                tempPerceptronConfiguration = null;
                neuralNetworkInterfaceClone = (INeuralNetworkInterfaceForTicTacToe<NeuralNetworkClass>) learningExperiment
                        .getaINeuralNetwork().clone();

                tempPerceptronInterface = null;

                if (learningExperiment.getaINeuralNetwork().getPerceptronConfiguration() != null) {
                    tempPerceptronConfiguration = (PerceptronConfigurationTicTacToe<NeuralNetworkClass>) learningExperiment
                            .getaINeuralNetwork().getPerceptronConfiguration().clone();
                    neuralNetworkInterfaceClone.setPerceptronConfiguration(tempPerceptronConfiguration);
                    tempPerceptronInterface = neuralNetworkInterfaceClone.getPerceptronInterface(); //TODO revisar esto
                }

                if (tempPerceptronConfiguration != null) {
                    //cargamos la red neuronal entrenada
                    File perceptronFile = new File(fileToProcess + ".ser");
                    if (!perceptronFile.exists()) {
                        throw new IllegalArgumentException(
                                "perceptron file must exists: " + perceptronFile.getCanonicalPath());
                    }
                    neuralNetworkInterfaceClone.loadOrCreatePerceptron(perceptronFile, true);
                }
            }
            boolean show = (delayPerMove > 0);

            GameTicTacToe<NeuralNetworkClass> game = new GameTicTacToe<>(tempPerceptronConfiguration,
                    learningExperiment.getLearningAlgorithm(), show, delayPerMove, null);//learningExperiment.getPlayerForStatistics());

            neuralNetworkInterfaces.add(neuralNetworkInterfaceClone);
            if (tempPerceptronConfiguration != null) {
                tdLambdaLearning
                        .add(learningExperiment.instanceOfTdLearninrgImplementation(tempPerceptronInterface)); //TODO revisar esto
            }
            games.add(game);
            results.add(new ThreadResult());
        }

        IntStream.range(0, simulations).parallel().forEach(i -> {
            // Si hay un perceptron ya entrenado, lo buscamos en el archivo.
            // En caso contrario creamos un perceptron vacio, inicializado al azar
            for (results.get(i).setProcesedGames(i); results.get(i).getProcesedGames() < gamesToPlay; results.get(i)
                    .addProcesedGames()) {
                games.get(i).newGameMenuItemActionPerformed(); //reset
                while (!games.get(i).isTerminalState()) {
                    switch (games.get(i).getCurrentlPlayer()) {
                    case PLAYER1: {
                        if (this.learningExperiment.isPlayer1Random()) {
                            this.learningExperiment.getaIRandom().playATurn(games.get(i), tdLambdaLearning.get(i));
                        } else {
                            neuralNetworkInterfaces.get(i).playATurn(games.get(i), tdLambdaLearning.get(i));
                        }
                        break;
                    }
                    case PLAYER2: {
                        if (this.learningExperiment.isPlayer2Random()) {
                            this.learningExperiment.getaIRandom().playATurn(games.get(i), tdLambdaLearning.get(i));
                        } else {
                            neuralNetworkInterfaces.get(i).playATurn(games.get(i), tdLambdaLearning.get(i));
                        }
                        break;
                    }
                    default:
                        throw new IllegalStateException("No se reconoce el jugador actual");
                    }

                }
                //calculamos estadisticas
                results.get(i).addWinner(games.get(i).getWinner());

            }
            games.get(i).dispose();
        });

        winRatePlayer1 = 0;
        winRatePlayer2 = 0;
        drawRate = 0;

        results.stream().forEach((result) -> {
            winRatePlayer1 += result.getWinRatePlayer1();
            winRatePlayer2 += result.getWinRatePlayer2();
            drawRate += result.getdrawRate();
        });

        winRatePlayer1 = winRatePlayer1 / (simulations * 1d);
        assert winRatePlayer1 <= 100;
        winRatePlayer2 = winRatePlayer2 / (simulations * 1d);
        drawRate = drawRate / (simulations * 1d);

        if (!results.isEmpty()) {
            //creamos un archivo de logs para acumular estadisticas
            Date now;
            if (dateForFileName != null) {
                now = dateForFileName;
            } else {
                now = new Date();
            }
            File logFile = new File(fileToProcess + "_" + dateFormater.format(now) + "_STATISTICS" + ".txt");

            try (PrintStream printStream = new PrintStream(logFile, "UTF-8")) {
                printStream.print("Gano: " + round(winRatePlayer1) + "% - Total de partidas: " + gamesToPlay
                        + " (promedios obtenidos con " + simulations + " simulaciones)");
                printStream.print("Gano: " + round(winRatePlayer2) + "% - Total de partidas: " + gamesToPlay
                        + " (promedios obtenidos con " + simulations + " simulaciones)");
                printStream.print("Gano: " + round(drawRate) + "% - Total de partidas: " + gamesToPlay
                        + " (promedios obtenidos con " + simulations + " simulaciones)");
                printStream.println("\nValores alcanzados:");

                printStream.println("\nPuntajes alcanzados (valor/veces):");

                printStream.println("Porcentaje de Juegos Ganados Jugador 1:" + winRatePlayer1);
                printStream.println("Porcentaje de Juegos Ganados Jugador 2:" + winRatePlayer2);
                printStream.println("Porcentaje de Juegos Empatadas:" + drawRate);
            }
            System.out.println("Finished.");
        }
    }

    /**
     *
     * @param experimentPath
     * @param delayPerMove
     */
    public void start(String experimentPath, int delayPerMove) {
        File experimentPathFile = new File(experimentPath);
        if (experimentPathFile.exists() && !experimentPathFile.isDirectory()) {
            throw new IllegalArgumentException("experimentPath must be a directory");
        }
        if (!experimentPathFile.exists()) {
            experimentPathFile.mkdirs();
        }
        try {
            learningMethod = null;
            if (learningExperiment != null) {
                this.experimentName = learningExperiment.getExperimentName();
            }
            initializeStatistics();
            run(experimentPath, delayPerMove);
        } catch (Exception ex) {
            Logger.getLogger(StatisticExperiment.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * @return the dateForFileName
     */
    protected Date getDateForFileName() {
        return dateForFileName;
    }

    /**
     * @param dateForFileName the dateForFileName to set
     */
    protected void setDateForFileName(Date dateForFileName) {
        this.dateForFileName = dateForFileName;
    }

    /**
     * @return the experimentName
     */
    protected String getExperimentName() {
        return experimentName;
    }

    /**
     * @param experimentName the experimentName to set
     */
    protected void setExperimentName(String experimentName) {
        this.experimentName = experimentName;
    }

    /**
     * @return the fileName
     */
    protected String getFileName() {
        return fileName;
    }

    /**
     * @param fileName the fileName to set
     */
    protected void setFileName(String fileName) {
        this.fileName = fileName;
    }

    /**
     * @return the gamesToPlay
     */
    protected int getGamesToPlay() {
        return gamesToPlay;
    }

    /**
     * @return the learningMethod
     */
    protected TDLambdaLearning getLearningMethod() {
        return learningMethod;
    }

    /**
     * @param learningMethod the learningMethod to set
     */
    protected void setLearningMethod(TDLambdaLearning learningMethod) {
        this.learningMethod = learningMethod;
    }

    /**
     *
     * @param dateFormater
     */
    protected void setSimpleDateFormat(SimpleDateFormat dateFormater) {
        this.dateFormater = dateFormater;
    }

    /**
     * @return the simulations
     */
    protected int getSimulations() {
        return simulations;
    }

    /**
     * @param threads the simulations to set
     */
    public void setSimulations(int threads) {
        this.simulations = threads;
    }

    /**
     * Se deben inicializar:
     * <ul>
     * <li>private int delayPerMove;</li>
     * <li>private IPlayingExperiment playingExperiment;</li>
     * <li>private String fileName;</li>
     * </ul>
     * <p>
     * Las siguientes variables se inicializan automaticamente, pero pueden ser
     * modificadas:
     * <ul>
     * <li>private int tileToWin;</li>
     * <li>private String experimentName;</li>
     * <li>private PerceptronConfiguration2048 perceptronConfiguration;</li>
     * <li>private TDLambdaLearning learningMethod;</li>
     * </ul>
     * <p>
     * @throws Exception
     */
    protected abstract void initializeStatistics() throws Exception;

    /**
     *
     * @param experimentPath
     * @param delayPerMove   <p>
     * @throws Exception
     */
    protected void run(String experimentPath, int delayPerMove) throws Exception {
        String dirPath = experimentPath + this.learningExperiment.getaINeuralNetwork().getLibName() + File.separator
                + experimentName + File.separator;
        File dirPathFile = new File(dirPath);
        if (!dirPathFile.exists()) {
            dirPathFile.mkdirs();
        }
        String filePath = dirPath + fileName;
        File randomPerceptronFile = new File(
                dirPath + this.getExperimentName() + LearningExperiment.RANDOM + ".ser");

        //hacemos estadisticas del perceptron random, si es necesario
        Map<File, StatisticForCalc> resultsRandom = new HashMap<>();
        System.out.print("Starting " + this.getExperimentName() + LearningExperiment.RANDOM + " Statistics... ");
        processFile(dirPath + this.getExperimentName() + LearningExperiment.RANDOM, delayPerMove);
        resultsRandom.put(randomPerceptronFile, getStatistics());

        //calculamos las estadisticas de los backup si es necesario
        File[] allFiles = (new File(dirPath)).listFiles();
        Arrays.sort(allFiles, (Object o1, Object o2) -> {
            if (((File) o1).lastModified() > ((File) o2).lastModified()) {
                return +1;
            } else if (((File) o1).lastModified() < ((File) o2).lastModified()) {
                return -1;
            } else {
                return 0;
            }
        });
        List<File> backupFiles = new ArrayList<>();
        Map<File, StatisticForCalc> resultsPerFile = new HashMap<>();
        for (File f : allFiles) {
            if (f.getName().matches(".*\\_BackupN\\-.*\\.ser")) {
                System.out.print("Starting " + f.getName() + " Statistics... ");
                processFile(dirPath + f.getName().replaceAll("\\.ser$", ""), delayPerMove);
                resultsPerFile.put(f, getStatistics());
                backupFiles.add(f);
            }
        }
        backupFiles.sort((Object o1, Object o2) -> {
            if (((File) o1).lastModified() > ((File) o2).lastModified()) {
                return +1;
            } else if (((File) o1).lastModified() < ((File) o2).lastModified()) {
                return -1;
            } else {
                return 0;
            }
        });

        exportToExcel(filePath, backupFiles, resultsPerFile, resultsRandom, randomPerceptronFile);

    }

    /**
     *
     * @param saveBackupEvery
     */
    protected void saveBackupEvery(int saveBackupEvery) {
        this.saveBackupEvery = saveBackupEvery;
    }

}