diff --git a/src/main/bat/runTwoEngines.bat b/src/main/bat/runTwoEngines.bat index 42e6793c6..ee11f58e0 100644 --- a/src/main/bat/runTwoEngines.bat +++ b/src/main/bat/runTwoEngines.bat @@ -1,34 +1,36 @@ -@rem ======================================================= -@rem Runs 2-engine match between Chess deployed to /LeokomChess and /LeokomChessTest -@rem Pre-requisites: -@rem variables.bat contains correct configuration -@rem ======================================================= - -%~d0 -cd %~p0 - -call variables.bat -SET RUNNABLE_JAR_DIRECTORY_2=%WINBOARD_INSTALLATION_PATH%\LeokomChessTest -SET RUN_JAR_PATH_2=%RUNNABLE_JAR_DIRECTORY_2%\Chess.jar - -@rem you may specify -Dblack.engine=Simple (or -Dblack=Simple if the second instance is Chess <= 0.3) -@rem for LegalPlayer you may specify -Dblack.depth=2 (if the second instance is Chess >= 0.4) -SET RUN_OPTIONS_2= -SET ENGINE_2=%JAVA_PATH% %RUN_OPTIONS_2% -jar %RUN_JAR_PATH_2% - -SET MATCHES_COUNT=1 - -@rem to turn on debug mode add -debug -@rem it will create winboard debug log - -@rem -mg means match game -@rem -testClaims disabled claims test in order to allow draw claim manually from the engine without adjudication -%WINBOARD_PATH% ^ --debug ^ --reuseFirst false ^ --mg %MATCHES_COUNT% ^ --fcp "%ENGINE%" ^ --fd "%RUNNABLE_JAR_DIRECTORY%" ^ --scp "%ENGINE_2%" ^ --sd "%RUNNABLE_JAR_DIRECTORY_2%" ^ --testClaims false +@rem ======================================================= +@rem Runs 2-engine match between Chess deployed to /LeokomChess and /LeokomChessTest +@rem Pre-requisites: +@rem variables.bat contains correct configuration +@rem ======================================================= + +%~d0 +cd %~p0 + +call variables.bat +SET RUNNABLE_JAR_DIRECTORY_2=%WINBOARD_INSTALLATION_PATH%\LeokomChessTest +SET RUN_JAR_PATH_2=%RUNNABLE_JAR_DIRECTORY_2%\Chess.jar + +@rem you may specify -Dblack.engine=brain.simple or brain.denormalized for Chess 0.5+ +@rem you may specify -Dblack.engine=Simple for Chess 0.4 +@rem you may specify -Dblack=Simple for Chess <= 0.3 +@rem for brain.normalized you may specify -Dblack.depth=2 (if the second instance is Chess >= 0.4) +SET RUN_OPTIONS_2= +SET ENGINE_2=%JAVA_PATH% %RUN_OPTIONS_2% -jar %RUN_JAR_PATH_2% + +SET MATCHES_COUNT=1 + +@rem to turn on debug mode add -debug +@rem it will create winboard debug log + +@rem -mg means match game +@rem -testClaims disabled claims test in order to allow draw claim manually from the engine without adjudication +%WINBOARD_PATH% ^ +-debug ^ +-reuseFirst false ^ +-mg %MATCHES_COUNT% ^ +-fcp "%ENGINE%" ^ +-fd "%RUNNABLE_JAR_DIRECTORY%" ^ +-scp "%ENGINE_2%" ^ +-sd "%RUNNABLE_JAR_DIRECTORY_2%" ^ +-testClaims false diff --git a/src/main/bat/variables.bat b/src/main/bat/variables.bat index 2d64e3872..a9434e9f5 100644 --- a/src/main/bat/variables.bat +++ b/src/main/bat/variables.bat @@ -1,22 +1,22 @@ -@rem ======================================================= -@rem Tunes common settings: -@rem * Winboard location -@rem * Java executable location -@rem * All derivatives and default engine settings -@rem ======================================================= - -@rem the variables should be tuned per target environment -SET WINBOARD_INSTALLATION_PATH=E:\Games\WinBoard-4.8.0 -SET JAVA_PATH=Q:\Program Files\Java\jdk1.8.0_162\bin\java.exe - -@rem UI that we use to run our Chess with -SET WINBOARD_PATH=%WINBOARD_INSTALLATION_PATH%\WinBoard\winboard.exe -@rem I use the Winboard installation as a Chess deployment target -@rem it should be equal to 'project.deployDirectory' property in pom.xml -SET RUNNABLE_JAR_DIRECTORY=%WINBOARD_INSTALLATION_PATH%\LeokomChess -SET RUN_JAR_PATH=%RUNNABLE_JAR_DIRECTORY%\Chess.jar -@rem you may pass -Dblack.engine=Simple to choose a different engine for blacks -@rem for LegalPlayer you may specify -Dblack.depth (1 or 2) -SET RUN_OPTIONS=-Dblack.depth=2 - +@rem ======================================================= +@rem Tunes common settings: +@rem * Winboard location +@rem * Java executable location +@rem * All derivatives and default engine settings +@rem ======================================================= + +@rem the variables should be tuned per target environment +SET WINBOARD_INSTALLATION_PATH=E:\Games\WinBoard-4.8.0 +SET JAVA_PATH=Q:\Program Files\Java\jdk1.8.0_162\bin\java.exe + +@rem UI that we use to run our Chess with +SET WINBOARD_PATH=%WINBOARD_INSTALLATION_PATH%\WinBoard\winboard.exe +@rem I use the Winboard installation as a Chess deployment target +@rem it should be equal to 'project.deployDirectory' property in pom.xml +SET RUNNABLE_JAR_DIRECTORY=%WINBOARD_INSTALLATION_PATH%\LeokomChess +SET RUN_JAR_PATH=%RUNNABLE_JAR_DIRECTORY%\Chess.jar +@rem you may pass -Dblack.engine=brain.simple or brain.denormalized to choose a different engine for blacks +@rem for brain.normalized you may specify -Dblack.depth (1 or 2) +SET RUN_OPTIONS=-Dblack.depth=2 + SET ENGINE=%JAVA_PATH% %RUN_OPTIONS% -jar %RUN_JAR_PATH% \ No newline at end of file diff --git a/src/main/java/com/leokom/chess/Game.java b/src/main/java/com/leokom/chess/Game.java index dbb6ef21e..56ca0e68c 100644 --- a/src/main/java/com/leokom/chess/Game.java +++ b/src/main/java/com/leokom/chess/Game.java @@ -5,6 +5,8 @@ import com.leokom.chess.player.Player; import org.apache.logging.log4j.LogManager; +import java.util.function.Function; + /** * Create & Run Game of Chess. * Author: Leonid @@ -14,6 +16,13 @@ public final class Game { private final Player whitePlayer; private final Player blackPlayer; + Game( Function< Side, Player > players ) { + this( + players.apply( Side.WHITE ), + players.apply( Side.BLACK ) + ); + } + /** * Initiate game between two players * @param whitePlayer white player diff --git a/src/main/java/com/leokom/chess/MainRunner.java b/src/main/java/com/leokom/chess/MainRunner.java index 85bace134..9b40b4259 100644 --- a/src/main/java/com/leokom/chess/MainRunner.java +++ b/src/main/java/com/leokom/chess/MainRunner.java @@ -1,8 +1,7 @@ package com.leokom.chess; -import com.leokom.chess.engine.Side; -import com.leokom.chess.player.Player; +import com.leokom.chess.players.CommandLinePlayers; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -30,20 +29,21 @@ private MainRunner() { * * engineName could be any of: *
    - *
  • Winboard
  • - *
  • Simple
  • - *
  • Legal
  • + *
  • ui.winboard
  • + *
  • brain.simple
  • + *
  • brain.denormalized
  • + *
  • brain.normalized
  • *
* * Default players: *
    - *
  • -Dwhite.engine=Winboard
  • - *
  • -Dblack.engine=Legal
  • + *
  • -Dwhite.engine=ui.winboard
  • + *
  • -Dblack.engine=brain.normalized
  • *
* *

* - * Optional parameters for LegalPlayer + * Optional parameters for brain.normalized *

    *
  • -Dwhite.depth=depth in plies
  • *
  • -Dblack.depth=depth in plies
  • @@ -70,8 +70,10 @@ private MainRunner() { public static void main( String[] args ) { try { logger.info( "Starting the chess..." ); - runGame(); - logger.info( "Chess are stopped. Bye-bye" ); + new Game( + new CommandLinePlayers() + ).run(); + logger.info( "Chess are stopped. Bye-bye" ); } catch ( RuntimeException re ) { //important to investigate issues @@ -86,12 +88,5 @@ public static void main( String[] args ) { } - private static void runGame() { - final Player whitePlayer = PlayerFactory.createPlayer( Side.WHITE ); - final Player blackPlayer = PlayerFactory.createPlayer( Side.BLACK ); - - new Game( whitePlayer, blackPlayer ).run(); - } - } diff --git a/src/main/java/com/leokom/chess/PlayerFactory.java b/src/main/java/com/leokom/chess/PlayerFactory.java deleted file mode 100644 index 744547de3..000000000 --- a/src/main/java/com/leokom/chess/PlayerFactory.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.leokom.chess; - -import com.leokom.chess.engine.Side; -import com.leokom.chess.player.Player; -import com.leokom.chess.player.legal.LegalPlayerSupplier; -import com.leokom.chess.player.legal.brain.simple.SimplePlayerSupplier; -import com.leokom.chess.player.winboard.WinboardPlayerSupplier; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.Optional; - -/** - * Create players for the chess game - * - * Author: Leonid - * Date-time: 06.05.14 22:45 - */ -final class PlayerFactory { - private PlayerFactory() {} - - private static Logger logger = LogManager.getLogger( PlayerFactory.class ); - - /** - * Chess system properties. - * Represent properties in format 'side.property' (like 'white.depth' or 'black.engine') - */ - static class ChessSystemProperty { - private final String propertyName; - - ChessSystemProperty( String propertyName ) { - this.propertyName = propertyName; - } - - Optional getFor( Side side ) { - return Optional.ofNullable( - System.getProperty( - String.format( "%s.%s", side.name().toLowerCase(), propertyName ) - ) - ); - } - } - - /** - * Create player for the side - * Basing on defaults or system properties. - * Defaults : - * WHITE: Winboard - * BLACK: Legal - * - * There are practical important limitations (not yet validated): - * - * Winboard vs Winboard game has no practical use (both will work with System.out) - * Winboard vs any other engine that uses System.out has no practical use (UCI?) - * - * LegalPlayer vs LegalPlayer is possible but can lead to StackOverflow due to - * no limits on move amount and single-threaded model of execution. - * - * LegalPlayer supports optional depth parameter. - * - * @param side side to create - * @return new instance of a player - */ - static Player createPlayer( Side side ) { - return new ChessSystemProperty("engine").getFor(side).map(engineName -> { - logger.info("Selecting an engine for Side = " + side + " by engine name = " + engineName); - switch (engineName) { - case "Legal": - return getLegalPlayerSupplier( side ); - case "Simple": - return new SimplePlayerSupplier(); - case "Winboard": - return new WinboardPlayerSupplier(); - default: - throw new IllegalArgumentException( "The engine is not supported: " + engineName); - } - }).orElseGet(() -> { - logger.info( "Selecting a default engine for Side = " + side ); - return side == Side.WHITE ? new WinboardPlayerSupplier() : getLegalPlayerSupplier( side ); - }).get(); - } - - private static LegalPlayerSupplier getLegalPlayerSupplier( Side side ) { - return new ChessSystemProperty("depth").getFor(side) - .map(Integer::valueOf) - .map(LegalPlayerSupplier::new) //takes depth parameter - .orElseGet(LegalPlayerSupplier::new); //without parameters, default constructor - } -} diff --git a/src/main/java/com/leokom/chess/player/legal/LegalPlayerSupplier.java b/src/main/java/com/leokom/chess/player/legal/LegalPlayerSupplier.java deleted file mode 100644 index 5e44dcce3..000000000 --- a/src/main/java/com/leokom/chess/player/legal/LegalPlayerSupplier.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.leokom.chess.player.legal; - -import com.leokom.chess.player.Player; -import com.leokom.chess.player.legal.brain.normalized.MasterEvaluator; -import com.leokom.chess.player.legal.brain.normalized.NormalizedBrain; - -import java.util.function.Supplier; - -public class LegalPlayerSupplier implements Supplier { - //this depth has been used for years - private static final int DEFAULT_DEPTH = 1; - private final int depth; - - public LegalPlayerSupplier() { - this(DEFAULT_DEPTH); - } - - public LegalPlayerSupplier( int depth ) { - this.depth = depth; - } - - public Player get() { - return new LegalPlayer( new NormalizedBrain<>( new MasterEvaluator(), depth ) ); - } -} diff --git a/src/main/java/com/leokom/chess/player/legal/brain/simple/SimplePlayerSupplier.java b/src/main/java/com/leokom/chess/player/legal/brain/simple/SimplePlayerSupplier.java deleted file mode 100644 index c537c93f0..000000000 --- a/src/main/java/com/leokom/chess/player/legal/brain/simple/SimplePlayerSupplier.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.leokom.chess.player.legal.brain.simple; - -import com.leokom.chess.player.Player; -import com.leokom.chess.player.legal.LegalPlayer; - -import java.util.function.Supplier; - -public class SimplePlayerSupplier implements Supplier { - - @Override - public Player get() { - return new LegalPlayer( new SimpleBrain() ); - } -} diff --git a/src/main/java/com/leokom/chess/player/winboard/WinboardPlayer.java b/src/main/java/com/leokom/chess/player/winboard/WinboardPlayer.java index 4ed8a4278..e99ae6138 100644 --- a/src/main/java/com/leokom/chess/player/winboard/WinboardPlayer.java +++ b/src/main/java/com/leokom/chess/player/winboard/WinboardPlayer.java @@ -162,7 +162,7 @@ private boolean canClaimDrawBeExecutedNow() { * @return instance of properly initialized Player against WinBoard-powered player * */ - static Player create() { + public static Player create() { //TODO: implement some singleton policy? final WinboardCommunicator communicator = new WinboardCommunicator(); return new WinboardPlayer( new WinboardCommanderImpl( communicator ) ); diff --git a/src/main/java/com/leokom/chess/player/winboard/WinboardPlayerSupplier.java b/src/main/java/com/leokom/chess/player/winboard/WinboardPlayerSupplier.java deleted file mode 100644 index 49d42c2f9..000000000 --- a/src/main/java/com/leokom/chess/player/winboard/WinboardPlayerSupplier.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.leokom.chess.player.winboard; - -import com.leokom.chess.player.Player; - -import java.util.function.Supplier; - -public class WinboardPlayerSupplier implements Supplier { - - @Override - public Player get() { - return WinboardPlayer.create(); - } -} diff --git a/src/main/java/com/leokom/chess/players/CommandLinePlayers.java b/src/main/java/com/leokom/chess/players/CommandLinePlayers.java new file mode 100644 index 000000000..85645df59 --- /dev/null +++ b/src/main/java/com/leokom/chess/players/CommandLinePlayers.java @@ -0,0 +1,103 @@ +package com.leokom.chess.players; + +import com.leokom.chess.engine.Side; +import com.leokom.chess.player.Player; +import com.leokom.chess.player.legal.LegalPlayer; +import com.leokom.chess.player.legal.brain.denormalized.DenormalizedBrain; +import com.leokom.chess.player.legal.brain.normalized.MasterEvaluator; +import com.leokom.chess.player.legal.brain.normalized.NormalizedBrain; +import com.leokom.chess.player.legal.brain.simple.SimpleBrain; +import com.leokom.chess.player.winboard.WinboardPlayer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; +import java.util.function.Function; + +/** + * Create players for the chess game based on command-line parameters + * + * Author: Leonid + * Date-time: 06.05.14 22:45 + */ +public final class CommandLinePlayers implements Function< Side, Player > { + private static Logger logger = LogManager.getLogger( CommandLinePlayers.class ); + + private final ChessSystemProperty engineProperty; + private final ChessSystemProperty depthProperty; + + public CommandLinePlayers() { + this.engineProperty = new ChessSystemProperty( "engine" ); + this.depthProperty = new ChessSystemProperty( "depth" ); + } + + /** + * Chess system properties. + * Represent properties in format 'side.property' (like 'white.depth' or 'black.engine') + */ + private static class ChessSystemProperty { + private final String propertyName; + + ChessSystemProperty( String propertyName ) { + this.propertyName = propertyName; + } + + Optional getFor( Side side ) { + return Optional.ofNullable( + System.getProperty( + String.format( "%s.%s", side.name().toLowerCase(), propertyName ) + ) + ); + } + } + + /** + * Create player for the side + * Basing on defaults or system properties. + * Defaults : + * WHITE: Winboard + * BLACK: brain.normalized + * + * There are practical important limitations (not yet validated): + * + * Winboard vs Winboard game has no practical use (both will work with System.out) + * Winboard vs any other engine that uses System.out has no practical use (UCI?) + * + * brain.* vs brain.* is possible but can lead to StackOverflow due to + * no limits on move amount and single-threaded model of execution + * (although some brains like brain.simple have internal limit on count of moves). + * + * brain.normalized supports optional depth parameter. + * + * @param side side to create + * @return new instance of a player + */ + @Override + public Player apply( Side side ) { + String engineName = engineProperty.getFor( side ).orElseGet( () -> { + logger.info( "Selecting a default engine for Side = " + side ); + return side == Side.WHITE ? "ui.winboard" : "brain.normalized"; + } ); + + return getPlayer( side, engineName ); + } + + private Player getPlayer( Side side, String engineName ) { + logger.info("Selecting an engine for Side = " + side + " by engine name = " + engineName); + switch (engineName) { + case "brain.normalized": + int depth = depthProperty.getFor(side) + .map(Integer::valueOf) + .orElse( 1 ); //this depth has been used for years + return new LegalPlayer( new NormalizedBrain<>( new MasterEvaluator(), depth ) ); + case "brain.denormalized": + return new LegalPlayer( new DenormalizedBrain() ); + case "brain.simple": + return new LegalPlayer( new SimpleBrain() ); + case "ui.winboard": + return WinboardPlayer.create(); + default: + throw new IllegalArgumentException( "The engine is not supported: " + engineName); + } + } +} diff --git a/src/main/java/com/leokom/chess/players/package-info.java b/src/main/java/com/leokom/chess/players/package-info.java new file mode 100644 index 000000000..e2a8e0f3b --- /dev/null +++ b/src/main/java/com/leokom/chess/players/package-info.java @@ -0,0 +1,5 @@ +/** + * Provide players for the Chess game. + * The goal of this package is to decide who will play chess in the current game + */ +package com.leokom.chess.players; \ No newline at end of file diff --git a/src/test/java/com/leokom/chess/PlayerFactoryTest.java b/src/test/java/com/leokom/chess/PlayerFactoryTest.java deleted file mode 100644 index a26a5b92a..000000000 --- a/src/test/java/com/leokom/chess/PlayerFactoryTest.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.leokom.chess; - -import com.leokom.chess.engine.Side; -import com.leokom.chess.player.Player; -import com.leokom.chess.player.winboard.WinboardPlayer; -import org.hamcrest.CoreMatchers; -import org.junit.*; -import org.junit.contrib.java.lang.system.RestoreSystemProperties; - -import static org.junit.Assert.*; - -public class PlayerFactoryTest { - //snapshots all system properties before a test, restores after it - @Rule - public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); - - @Test - public void noSystemPropertiesDefaultPlayerBlack() { - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertIsLegal( player ); - } - - @Test - public void canSelectSimpleEngineForWhite() { - System.setProperty( "white.engine", "Simple" ); - - final Player player = PlayerFactory.createPlayer( Side.WHITE ); - assertIsSimple( player ); - } - - @Test( expected = IllegalArgumentException.class ) - public void failFastOnUnsupportedEngine() { - System.setProperty( "white.engine", "Unsupported" ); - - PlayerFactory.createPlayer( Side.WHITE ); - } - - private void assertIsSimple(Player player) { - assertEquals( "LegalPlayer : SimpleBrain", player.name() ); - } - - @Test - public void canSelectWinboardForBlack() { - System.setProperty( "black.engine", "Winboard" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertTrue( player instanceof WinboardPlayer ); - } - - @Test - public void noSystemPropertiesDefaultPlayerWhite() { - final Player player = PlayerFactory.createPlayer( Side.WHITE ); - assertTrue( player instanceof WinboardPlayer ); - } - - @Test - public void legalSelected() { - System.setProperty( "black", "Legal" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertIsLegal( player ); - } - - private void assertIsLegal( Player player ) { - assertThat( player.name(), CoreMatchers.startsWith( "LegalPlayer" ) ); - } - - @Test - public void legalSelectedWhite() { - System.setProperty( "white.engine", "Legal" ); - - final Player player = PlayerFactory.createPlayer( Side.WHITE ); - assertIsLegal( player ); - } - - @Test - public void depth2FromCommandLineRespectedForWhite() { - System.setProperty( "white.engine", "Legal" ); - System.setProperty( "white.depth", "2" ); - - final Player player = PlayerFactory.createPlayer( Side.WHITE ); - assertDepth( player, 2 ); - } - - @Test - public void depth1FromCommandLineRespectedForWhite() { - System.setProperty( "white.engine", "Legal" ); - System.setProperty( "white.depth", "1" ); - - final Player player = PlayerFactory.createPlayer( Side.WHITE ); - assertDepth( player, 1 ); - } - - @Test - public void depth1FromCommandLineRespectedForBlack() { - System.setProperty( "black.engine", "Legal" ); - System.setProperty( "black.depth", "1" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertDepth( player, 1 ); - } - - @Test - public void depth2FromCommandLineRespectedForBlack() { - System.setProperty( "black.engine", "Legal" ); - System.setProperty( "black.depth", "2" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertDepth( player, 2 ); - } - - @Test - public void legalPlayerDepthCanBeProvidedEvenIfEngineIsNotProvided() { - //because legal is default one - System.setProperty( "black.depth", "2" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertDepth( player, 2 ); - } - - @Test - public void defaultDepthIs1() { - System.setProperty( "black.engine", "Legal" ); - - final Player player = PlayerFactory.createPlayer( Side.BLACK ); - assertDepth( player, 1 ); - } - - private void assertDepth( Player player, int expectedDepth ) { - //shallow yet good enough check - assertThat( player.name(), CoreMatchers.containsString( String.valueOf( expectedDepth ) ) ); - } -} \ No newline at end of file diff --git a/src/test/java/com/leokom/chess/SimulatorIT.java b/src/test/java/com/leokom/chess/SimulatorIT.java index 5dec029b8..6590ce155 100644 --- a/src/test/java/com/leokom/chess/SimulatorIT.java +++ b/src/test/java/com/leokom/chess/SimulatorIT.java @@ -5,14 +5,13 @@ import com.leokom.chess.engine.Side; import com.leokom.chess.player.Player; import com.leokom.chess.player.legal.LegalPlayer; -import com.leokom.chess.player.legal.LegalPlayerSupplier; import com.leokom.chess.player.legal.brain.common.Evaluator; import com.leokom.chess.player.legal.brain.common.EvaluatorType; import com.leokom.chess.player.legal.brain.denormalized.DenormalizedBrain; import com.leokom.chess.player.legal.brain.normalized.MasterEvaluator; import com.leokom.chess.player.legal.brain.normalized.MasterEvaluatorBuilder; import com.leokom.chess.player.legal.brain.normalized.NormalizedBrain; -import com.leokom.chess.player.legal.brain.simple.SimplePlayerSupplier; +import com.leokom.chess.player.legal.brain.simple.SimpleBrain; import org.junit.Ignore; import org.junit.Test; @@ -131,17 +130,17 @@ private void programPlayers( Position position, Position ... positions ) { when( second.getPosition() ).thenReturn( position, positions ); } - //we expect the default brain of the legal player is much smarter than the simple one + //we expect the normalized brain of the legal player is much smarter than the simple one @Test public void legalVsSimpleStatistics() { - final SimulatorStatistics statistics = new Simulator( new LegalPlayerSupplier(), new SimplePlayerSupplier() ).run(); + final SimulatorStatistics statistics = new Simulator( new LegalPlayer( new NormalizedBrain<>( new MasterEvaluator() ) ), new LegalPlayer( new SimpleBrain() ) ).run(); assertEquals( new SimulatorStatistics( 2, 2, 0 ), statistics ); } @Test public void simpleVsSimpleStatistics() { - final SimulatorStatistics statistics = new Simulator( new SimplePlayerSupplier(), new SimplePlayerSupplier() ).run(); + final SimulatorStatistics statistics = new Simulator( new LegalPlayer( new SimpleBrain() ), new LegalPlayer( new SimpleBrain() ) ).run(); //now simple vs simple correctly draws at the second move assertEquals( new SimulatorStatistics( 2, 0, 0 ), statistics ); diff --git a/src/test/java/com/leokom/chess/player/legal/LegalPlayerNameTest.java b/src/test/java/com/leokom/chess/player/legal/LegalPlayerNameTest.java index 749b2c987..3eed9a707 100644 --- a/src/test/java/com/leokom/chess/player/legal/LegalPlayerNameTest.java +++ b/src/test/java/com/leokom/chess/player/legal/LegalPlayerNameTest.java @@ -1,6 +1,6 @@ package com.leokom.chess.player.legal; -import com.leokom.chess.player.legal.brain.simple.SimplePlayerSupplier; +import com.leokom.chess.player.legal.brain.simple.SimpleBrain; import com.leokom.chess.player.legal.brain.denormalized.DenormalizedBrain; import com.leokom.chess.player.legal.brain.normalized.MasterEvaluator; import com.leokom.chess.player.legal.brain.normalized.NormalizedBrain; @@ -21,6 +21,6 @@ public void normalizedBrain() { @Test public void simpleBrain() { - assertEquals( "LegalPlayer : SimpleBrain", new SimplePlayerSupplier().get().name() ); + assertEquals( "LegalPlayer : SimpleBrain", new LegalPlayer( new SimpleBrain() ).name() ); } } diff --git a/src/test/java/com/leokom/chess/player/legal/brain/simple/SimpleBrainTest.java b/src/test/java/com/leokom/chess/player/legal/brain/simple/SimpleBrainTest.java index ea44ee660..3396065c6 100644 --- a/src/test/java/com/leokom/chess/player/legal/brain/simple/SimpleBrainTest.java +++ b/src/test/java/com/leokom/chess/player/legal/brain/simple/SimpleBrainTest.java @@ -4,6 +4,7 @@ import com.leokom.chess.engine.Move; import com.leokom.chess.player.Player; import com.leokom.chess.player.PlayerBuilder; +import com.leokom.chess.player.legal.LegalPlayer; import org.junit.Before; import org.junit.Test; @@ -17,7 +18,7 @@ public class SimpleBrainTest { @Before public void prepare() { - simplePlayer = new SimplePlayerSupplier().get(); + simplePlayer = new LegalPlayer( new SimpleBrain() ); } @Test diff --git a/src/test/java/com/leokom/chess/players/CommandLinePlayersTest.java b/src/test/java/com/leokom/chess/players/CommandLinePlayersTest.java new file mode 100644 index 000000000..085210c40 --- /dev/null +++ b/src/test/java/com/leokom/chess/players/CommandLinePlayersTest.java @@ -0,0 +1,141 @@ +package com.leokom.chess.players; + +import com.leokom.chess.engine.Side; +import com.leokom.chess.player.Player; +import com.leokom.chess.player.winboard.WinboardPlayer; +import org.hamcrest.CoreMatchers; +import org.junit.*; +import org.junit.contrib.java.lang.system.RestoreSystemProperties; + +import static org.junit.Assert.*; + +public class CommandLinePlayersTest { + //snapshots all system properties before a test, restores after it + @Rule + public final RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(); + + @Test + public void noSystemPropertiesDefaultPlayerBlack() { + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertHasNormalizedBrain( player ); + } + + @Test + public void canSelectSimpleBrainForWhite() { + System.setProperty( "white.engine", "brain.simple" ); + + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertIsSimple( player ); + } + + @Test + public void canSelectDenormalizedBrainForWhite() { + System.setProperty( "white.engine", "brain.denormalized" ); + + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertThat( player.name(), CoreMatchers.containsString( "Denormalized" ) ); + } + + @Test( expected = IllegalArgumentException.class ) + public void failFastOnUnsupportedEngine() { + System.setProperty( "white.engine", "Unsupported" ); + + new CommandLinePlayers().apply( Side.WHITE ); + } + + private void assertIsSimple(Player player) { + assertEquals( "LegalPlayer : SimpleBrain", player.name() ); + } + + @Test + public void canSelectWinboardForBlack() { + System.setProperty( "black.engine", "ui.winboard" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertTrue( player instanceof WinboardPlayer ); + } + + @Test + public void noSystemPropertiesDefaultPlayerWhite() { + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertTrue( player instanceof WinboardPlayer ); + } + + @Test + public void normalizedSelectedBlack() { + System.setProperty( "black.engine", "brain.normalized" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertHasNormalizedBrain( player ); + } + + private void assertHasNormalizedBrain(Player player ) { + assertThat( player.name(), CoreMatchers.startsWith( "LegalPlayer : NormalizedBrain" ) ); + } + + @Test + public void normalizedSelectedWhite() { + System.setProperty( "white.engine", "brain.normalized" ); + + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertHasNormalizedBrain( player ); + } + + @Test + public void depth2FromCommandLineRespectedForWhite() { + System.setProperty( "white.engine", "brain.normalized" ); + System.setProperty( "white.depth", "2" ); + + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertDepth( player, 2 ); + } + + @Test + public void depth1FromCommandLineRespectedForWhite() { + System.setProperty( "white.engine", "brain.normalized" ); + System.setProperty( "white.depth", "1" ); + + final Player player = new CommandLinePlayers().apply( Side.WHITE ); + assertDepth( player, 1 ); + } + + @Test + public void depth1FromCommandLineRespectedForBlack() { + System.setProperty( "black.engine", "brain.normalized" ); + System.setProperty( "black.depth", "1" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertDepth( player, 1 ); + } + + @Test + public void depth2FromCommandLineRespectedForBlack() { + System.setProperty( "black.engine", "brain.normalized" ); + System.setProperty( "black.depth", "2" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertDepth( player, 2 ); + } + + @Test + public void normalizedBrainDepthCanBeProvidedEvenIfEngineIsNotProvided() { + //because normalized is default one + System.setProperty( "black.depth", "2" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertDepth( player, 2 ); + } + + @Test + public void defaultDepthIs1() { + System.setProperty( "black.engine", "brain.normalized" ); + + final Player player = new CommandLinePlayers().apply( Side.BLACK ); + assertDepth( player, 1 ); + } + + private void assertDepth( Player player, int expectedDepth ) { + //shallow yet good enough check + assertThat( player.name(), CoreMatchers.containsString( String.valueOf( expectedDepth ) ) ); + } +} \ No newline at end of file