package com.chess.pgn; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import com.chess.engine.classic.board.Board; import com.chess.engine.classic.board.Move; import com.chess.engine.classic.player.Player; public class MySqlGamePersistence implements PGNPersistence { private final Connection dbConnection; private static MySqlGamePersistence INSTANCE = new MySqlGamePersistence(); private static final String JDBC_DRIVER = "com.mysql.cj.jdbc.Driver"; private static final String DB_URL = "jdbc:mysql://localhost/chessgames"; private static final String USER = "root"; private static final String PASS = "powerpc123"; private static final String NEXT_BEST_MOVE_QUERY = "SELECT SUBSTR(g1.moves, LENGTH('%s') + %d, INSTR(SUBSTR(g1.moves, LENGTH('%s') + %d, LENGTH(g1.moves)), ',') - 1), " + "COUNT(*) FROM game g1 WHERE g1.moves LIKE '%s%%' AND (outcome = '%s') GROUP BY substr(g1.moves, LENGTH('%s') + %d, " + "INSTR(substr(g1.moves, LENGTH('%s') + %d, LENGTH(g1.moves)), ',') - 1) ORDER BY 2 DESC"; private MySqlGamePersistence() { this.dbConnection = createDBConnection(); createGameTable(); createIndex("outcome", "OutcomeIndex"); createIndex("moves", "MoveIndex"); // createOutcomeIndex(); // createMovesIndex(); } private static Connection createDBConnection() { try { Class.forName(JDBC_DRIVER); return DriverManager.getConnection(DB_URL, USER, PASS); } catch (final ClassNotFoundException | SQLException e) { throw new RuntimeException(e); } } public static MySqlGamePersistence get() { return INSTANCE; } @Override public void persistGame(final Game game) { executePersist(game); } @Override public Move getNextBestMove(final Board board, final Player player, final String gameText) { return queryBestMove(board, player, gameText); } private Move queryBestMove(final Board board, final Player player, final String gameText) { String bestMove = ""; String count = "0"; try { final int offSet = gameText.isEmpty() ? 1 : 3; final String sqlString = String.format(NEXT_BEST_MOVE_QUERY, gameText, offSet, gameText, offSet, gameText, player.getAlliance().name(), gameText, offSet, gameText, offSet); System.out.println(sqlString); final Statement gameStatement = this.dbConnection.createStatement(); gameStatement.execute(sqlString); final ResultSet rs2 = gameStatement.getResultSet(); if(rs2.next()) { bestMove = rs2.getString(1); count = rs2.getString(2); } gameStatement.close(); } catch (final SQLException e) { e.printStackTrace(); } System.out.println("\tselected book move = " +bestMove+ " with " +count+ " hits"); return PGNUtilities.createMove(board, bestMove); } private void createGameTable() { try { final Statement statement = this.dbConnection.createStatement(); statement.execute("CREATE TABLE IF NOT EXISTS Game(id int primary key, outcome varchar(10), moves varchar(3072));"); statement.close(); } catch (final SQLException e) { e.printStackTrace(); } } private void createIndex(final String columnName, final String indexName) { try { final String sqlString = "SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_CATALOG = 'def' AND " + " TABLE_SCHEMA = DATABASE() AND TABLE_NAME = \"game\" AND INDEX_NAME = \"" +indexName+"\""; final Statement gameStatement = this.dbConnection.createStatement(); gameStatement.execute(sqlString); final ResultSet resultSet = gameStatement.getResultSet(); if(!resultSet.isBeforeFirst() ) { final Statement indexStatement = this.dbConnection.createStatement(); indexStatement.execute("CREATE INDEX " +indexName+ " on Game(" +columnName+ ");\n"); indexStatement.close(); } gameStatement.close(); } catch (final SQLException e) { System.out.println("CREATE INDEX " +indexName+ " on Game(" +columnName+ ");\n"); e.printStackTrace(); } } private void createOutcomeIndex() { try { final String sqlString = "SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_CATALOG = 'def' AND TABLE_SCHEMA = DATABASE() AND TABLE_NAME = \"game\" AND INDEX_NAME = \"OutcomeIndex\""; final Statement gameStatement = this.dbConnection.createStatement(); gameStatement.execute(sqlString); final ResultSet resultSet = gameStatement.getResultSet(); if(!resultSet.isBeforeFirst() ) { final Statement indexStatement = this.dbConnection.createStatement(); indexStatement.execute("CREATE INDEX OutcomeIndex on Game(outcome);\n"); indexStatement.close(); } gameStatement.close(); } catch (final SQLException e) { e.printStackTrace(); } } private void createMovesIndex() { try { final String sqlString = "SELECT * FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_CATALOG = 'def' AND TABLE_SCHEMA = DATABASE() AND TABLE_NAME = \"game\" AND INDEX_NAME = \"MoveIndex\""; final Statement gameStatement = this.dbConnection.createStatement(); gameStatement.execute(sqlString); final ResultSet resultSet = gameStatement.getResultSet(); if(!resultSet.isBeforeFirst() ) { final Statement indexStatement = this.dbConnection.createStatement(); indexStatement.execute("CREATE INDEX MoveIndex on Game(moves);\n"); indexStatement.close(); } gameStatement.close(); } catch (final SQLException e) { e.printStackTrace(); } } public int getMaxGameRow() { int maxId = 0; try { final String sqlString = "SELECT MAX(ID) FROM Game"; final Statement gameStatement = this.dbConnection.createStatement(); gameStatement.execute(sqlString); final ResultSet rs2 = gameStatement.getResultSet(); if(rs2.next()) { maxId = rs2.getInt(1); } gameStatement.close(); } catch (final SQLException e) { e.printStackTrace(); } return maxId; } private void executePersist(final Game game) { try { final String gameSqlString = "INSERT INTO Game(id, outcome, moves) VALUES(?, ?, ?);"; final PreparedStatement gameStatement = this.dbConnection.prepareStatement(gameSqlString); gameStatement.setInt(1, getMaxGameRow() + 1); gameStatement.setString(2, game.getWinner()); gameStatement.setString(3, game.getMoves().toString().replaceAll("\\[", "").replaceAll("\\]", "")); gameStatement.executeUpdate(); gameStatement.close(); } catch (final SQLException e) { e.printStackTrace(); } } }