From dc702f75849aa094aa22f3b2d3137f991513497f Mon Sep 17 00:00:00 2001 From: djmil Date: Mon, 18 Sep 2023 13:33:49 +0200 Subject: [PATCH] GameState a base state class for GameProposal, GameBoard and GameResult also will be used as a base building block for GameBoardView --- .../contracts/GameBoardCommand.java | 8 +- .../java/djmil/cordacheckers/states/Game.java | 24 ------ .../cordacheckers/states/GameBoardState.java | 77 ++++++------------- .../states/GameProposalState.java | 28 +------ .../cordacheckers/states/GameResultState.java | 73 ++++++++---------- .../djmil/cordacheckers/states/GameState.java | 59 ++++++++++++++ .../gameboard/GameBoardView.java | 2 +- .../cordacheckers/gameboard/ListFlow.java | 2 +- .../gameproposal/CommandFlow.java | 2 +- .../gameresult/GameResultView.java | 6 +- 10 files changed, 125 insertions(+), 156 deletions(-) delete mode 100644 corda/contracts/src/main/java/djmil/cordacheckers/states/Game.java create mode 100644 corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java index ea61155..e81a117 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/contracts/GameBoardCommand.java @@ -75,8 +75,8 @@ public class GameBoardCommand implements Command { requireThat(trx.getOutputContractStates().size() == 1, SURRENDER_OUTPUT_STATE); final var outGameResultState = getSingleOutputState(trx, GameResultState.class); - requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameResultState.getWhitePlayerName()) == 0, IN_OUT_PARTICIPANTS); - requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameResultState.getBlackPlayerName()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameResultState.getWinnerName()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameResultState.getOpponentName()) == 0, IN_OUT_PARTICIPANTS); } public static void validateMoveTrx(UtxoLedgerTransaction trx) { @@ -97,8 +97,8 @@ public class GameBoardCommand implements Command { requireThat(trx.getOutputContractStates().size() == 1, FINAL_MOVE_OUTPUT_STATE); final var outGameResultState = getSingleOutputState(trx, GameResultState.class); - requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameResultState.getWhitePlayerName()) == 0, IN_OUT_PARTICIPANTS); - requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameResultState.getBlackPlayerName()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getWhitePlayerName().compareTo(outGameResultState.getWinnerName()) == 0, IN_OUT_PARTICIPANTS); + requireThat(inGameBoardState.getBlackPlayerName().compareTo(outGameResultState.getOpponentName()) == 0, IN_OUT_PARTICIPANTS); } static final String BAD_ACTIONMOVE_CONSTRUCTOR = "Bad constructor for Action.MOVE"; diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/Game.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/Game.java deleted file mode 100644 index 28c4670..0000000 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/Game.java +++ /dev/null @@ -1,24 +0,0 @@ -package djmil.cordacheckers.states; - -import java.util.UUID; - -import org.jetbrains.annotations.NotNull; - -import net.corda.v5.base.annotations.CordaSerializable; -import net.corda.v5.base.types.MemberX500Name; - -@CordaSerializable -public interface Game { - - @NotNull - MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved; - - @NotNull - UUID getGameUuid(); - - public static class NotInvolved extends RuntimeException { - public NotInvolved(MemberX500Name myName, Class clazz, UUID uuid) { - super(myName +" not involved in " +clazz.getSimpleName() +" UUID " +uuid); - } - } -} diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java index 0010642..33dc288 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameBoardState.java @@ -11,10 +11,9 @@ import djmil.cordacheckers.states.Piece.Color; import net.corda.v5.base.annotations.ConstructorForDeserialization; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.BelongsToContract; -import net.corda.v5.ledger.utxo.ContractState; @BelongsToContract(GameBoardContract.class) -public class GameBoardState implements ContractState, Game { +public class GameBoardState extends GameState { private final MemberX500Name whitePlayerName; private final MemberX500Name blackPlayerName; @@ -22,14 +21,10 @@ public class GameBoardState implements ContractState, Game { private final Piece.Color moveColor; private final Integer moveNumber; private final Map board; - private final String message; - private final UUID gameUuid; - private final List participants; + public GameBoardState(GameProposalState gameProposalState) { + super(gameProposalState.gameUuid, gameProposalState.message, gameProposalState.participants); - public GameBoardState( - GameProposalState gameProposalState - ) { this.whitePlayerName = gameProposalState.getWhitePlayerName(); this.blackPlayerName = gameProposalState.getBlackPlayerName(); @@ -37,15 +32,12 @@ public class GameBoardState implements ContractState, Game { this.moveColor = Piece.Color.WHITE; this.moveNumber = 0; this.board = new LinkedHashMap(initialBoard); - this.message = null; - - this.gameUuid = gameProposalState.getGameUuid(); - this.participants = gameProposalState.getParticipants(); } public GameBoardState( - GameBoardState oldGameBoardState, Map newBoard, Piece.Color moveColor - ) { + GameBoardState oldGameBoardState, Map newBoard, Piece.Color moveColor) { + super(oldGameBoardState.gameUuid, oldGameBoardState.message, oldGameBoardState.participants); + this.whitePlayerName = oldGameBoardState.getWhitePlayerName(); this.blackPlayerName = oldGameBoardState.getBlackPlayerName(); @@ -53,32 +45,30 @@ public class GameBoardState implements ContractState, Game { this.moveColor = moveColor; this.moveNumber = oldGameBoardState.getMoveNumber() +1; this.board = newBoard; - this.message = null; - - this.gameUuid = oldGameBoardState.getGameUuid(); - this.participants = oldGameBoardState.getParticipants(); } @ConstructorForDeserialization public GameBoardState(MemberX500Name whitePlayerName, MemberX500Name blackPlayerName, Color moveColor, Integer moveNumber, Map board, String message, UUID gameUuid, List participants) { + super(gameUuid, message, participants); + this.whitePlayerName = whitePlayerName; this.blackPlayerName = blackPlayerName; this.moveColor = moveColor; this.moveNumber = moveNumber; this.board = board; - this.message = message; - this.gameUuid = gameUuid; - this.participants = participants; } - public MemberX500Name getWhitePlayerName() { - return whitePlayerName; - } - - public MemberX500Name getBlackPlayerName() { - return blackPlayerName; + public MemberX500Name getMovePlayerName() { + switch (moveColor) { + case WHITE: + return whitePlayerName; + case BLACK: + return blackPlayerName; + default: + throw new Piece.Color.UnknownException(); + } } public Piece.Color getMoveColor() { @@ -93,16 +83,9 @@ public class GameBoardState implements ContractState, Game { return board; } - public String getMessage() { - return message; - } - - public UUID getGameUuid() { - return gameUuid; - } - - public List getParticipants() { - return participants; + @Override + public MemberX500Name getWhitePlayerName() { + return whitePlayerName; } @Override @@ -113,26 +96,10 @@ public class GameBoardState implements ContractState, Game { if (blackPlayerName.compareTo(myName) == 0) return whitePlayerName; - throw new Game.NotInvolved(myName, GameBoardState.class, this.gameUuid); - } - - public Piece.Color getCounterpartyColor(MemberX500Name myName) throws NotInvolved { - final MemberX500Name opponentName = getCounterpartyName(myName); - - return getWhitePlayerName().compareTo(opponentName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK; - } - - public MemberX500Name getMovePlayerName() { - switch (moveColor) { - case WHITE: - return whitePlayerName; - case BLACK: - return blackPlayerName; - default: - throw new Piece.Color.UnknownException(); - } + throw new GameState.NotInvolved(myName, GameBoardState.class, this.gameUuid); } + // TODO: move to contract public final static Map initialBoard = Map.ofEntries( // Inspired by Checkers notation rules: https://www.bobnewell.net/nucleus/checkers.php Map.entry( 1, new Piece(Piece.Color.BLACK, Piece.Type.MAN)), diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java index b7c50bf..3ef29b1 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameProposalState.java @@ -8,17 +8,13 @@ import djmil.cordacheckers.contracts.GameProposalContract; import net.corda.v5.base.annotations.ConstructorForDeserialization; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.BelongsToContract; -import net.corda.v5.ledger.utxo.ContractState; @BelongsToContract(GameProposalContract.class) -public class GameProposalState implements ContractState, Game { +public class GameProposalState extends GameState { private final MemberX500Name issuer; private final MemberX500Name acquier; private final Piece.Color acquierColor; - private final String message; - private final UUID gameUuid; - private final List participants; @ConstructorForDeserialization public GameProposalState( @@ -29,12 +25,10 @@ public class GameProposalState implements ContractState, Game { UUID gameUuid, List participants ) { + super(gameUuid, message, participants); this.issuer = issuer; this.acquier = acquier; this.acquierColor = acquierColor; - this.message = message; - this.gameUuid = gameUuid; - this.participants = participants; } public MemberX500Name getIssuer() { @@ -49,26 +43,10 @@ public class GameProposalState implements ContractState, Game { return acquierColor; } - public String getMessage() { - return message; - } - - public UUID getGameUuid() { - return gameUuid; - } - - public List getParticipants() { - return participants; - } - public MemberX500Name getWhitePlayerName() { return acquierColor == Piece.Color.WHITE ? getAcquier() : getIssuer(); } - public MemberX500Name getBlackPlayerName() { - return acquierColor == Piece.Color.BLACK ? getAcquier() : getIssuer(); - } - @Override public MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved { if (issuer.compareTo(myName) == 0) @@ -77,7 +55,7 @@ public class GameProposalState implements ContractState, Game { if (acquier.compareTo(myName) == 0) return issuer; - throw new Game.NotInvolved(myName, GameProposalState.class, this.gameUuid); + throw new GameState.NotInvolved(myName, GameProposalState.class, this.gameUuid); } } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java index 7b1c510..3ea7414 100644 --- a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameResultState.java @@ -8,68 +8,57 @@ import djmil.cordacheckers.contracts.GameResultContract; import net.corda.v5.base.annotations.ConstructorForDeserialization; import net.corda.v5.base.types.MemberX500Name; import net.corda.v5.ledger.utxo.BelongsToContract; -import net.corda.v5.ledger.utxo.ContractState; @BelongsToContract(GameResultContract.class) -public class GameResultState implements ContractState, Game { +public class GameResultState extends GameState { - private final MemberX500Name whitePlayerName; - private final MemberX500Name blackPlayerName; + private final MemberX500Name winnerName; + private final MemberX500Name opponentName; - private final Piece.Color victoryColor; - - private final UUID gameUuid; - private final List participants; + private final Piece.Color winnerColor; @ConstructorForDeserialization - public GameResultState(MemberX500Name whitePlayerName, MemberX500Name blackPlayerName, Piece.Color victoryColor, - UUID gameUuid, List participants) { - this.whitePlayerName = whitePlayerName; - this.blackPlayerName = blackPlayerName; - this.victoryColor = victoryColor; - this.gameUuid = gameUuid; - this.participants = participants; + public GameResultState(MemberX500Name winnerName, MemberX500Name opponentName, Piece.Color winnerColor, + UUID gameUuid, String message, List participants) { + super(gameUuid, message, participants); + this.winnerName = winnerName; + this.opponentName = opponentName; + this.winnerColor = winnerColor; } - public GameResultState(GameBoardState stateGameBoard, Piece.Color victoryColor) { - this.whitePlayerName = stateGameBoard.getWhitePlayerName(); - this.blackPlayerName = stateGameBoard.getBlackPlayerName(); - this.victoryColor = victoryColor; - - this.gameUuid = stateGameBoard.getGameUuid(); - this.participants = stateGameBoard.getParticipants(); + public GameResultState(GameBoardState gameBoardState, Piece.Color victoryColor) { + super(gameBoardState.gameUuid, null, gameBoardState.participants); + this.winnerName = gameBoardState.getWhitePlayerName(); + this.opponentName = gameBoardState.getBlackPlayerName(); + this.winnerColor = victoryColor; } - public MemberX500Name getWhitePlayerName() { - return whitePlayerName; + public MemberX500Name getWinnerName() { + return winnerName; } - public MemberX500Name getBlackPlayerName() { - return blackPlayerName; + public MemberX500Name getOpponentName() { + return opponentName; } - public Piece.Color getVictoryColor() { - return victoryColor; - } - - public UUID getGameUuid() { - return gameUuid; - } - - @Override - public List getParticipants() { - return participants; + public Piece.Color getWinnerColor() { + return winnerColor; } @Override public MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved { - if (whitePlayerName.compareTo(myName) == 0) - return blackPlayerName; + if (winnerName.compareTo(myName) == 0) + return opponentName; - if (blackPlayerName.compareTo(myName) == 0) - return whitePlayerName; + if (opponentName.compareTo(myName) == 0) + return winnerName; - throw new Game.NotInvolved(myName, GameResultState.class, this.gameUuid); + throw new GameState.NotInvolved(myName, GameResultState.class, this.gameUuid); + } + + @Override + MemberX500Name getWhitePlayerName() { + return winnerColor == Piece.Color.WHITE ? winnerName : opponentName; } } diff --git a/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java new file mode 100644 index 0000000..61a3b9d --- /dev/null +++ b/corda/contracts/src/main/java/djmil/cordacheckers/states/GameState.java @@ -0,0 +1,59 @@ +package djmil.cordacheckers.states; + +import java.security.PublicKey; +import java.util.List; +import java.util.UUID; + +import org.jetbrains.annotations.NotNull; + +import net.corda.v5.base.annotations.CordaSerializable; +import net.corda.v5.base.types.MemberX500Name; +import net.corda.v5.ledger.utxo.ContractState; + +@CordaSerializable +public abstract class GameState implements ContractState { + final UUID gameUuid; + final String message; + final List participants; + + @NotNull + abstract MemberX500Name getWhitePlayerName(); + + @NotNull + abstract MemberX500Name getCounterpartyName(MemberX500Name myName) throws NotInvolved; + + GameState(UUID gameUuid, String message, List participants) { + this.gameUuid = gameUuid; + this.message = message; + this.participants = participants; + } + + public UUID getGameUuid() { + return gameUuid; + } + + public String getMessage() { + return message; + } + + public List getParticipants() { + return participants; + } + + public MemberX500Name getBlackPlayerName() { + return getCounterpartyName(getWhitePlayerName()); + } + + public Piece.Color getCounterpartyColor(MemberX500Name myName) throws NotInvolved { + final MemberX500Name opponentName = getCounterpartyName(myName); + + return getWhitePlayerName().compareTo(opponentName) == 0 ? Piece.Color.WHITE : Piece.Color.BLACK; + } + + public static class NotInvolved extends RuntimeException { + public NotInvolved(MemberX500Name name, Class clazz, UUID uuid) { + super(name +" is not involved in " +clazz.getSimpleName() +" UUID " +uuid); + } + } + +} diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/GameBoardView.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/GameBoardView.java index b73a4f3..46a04a9 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/GameBoardView.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/GameBoardView.java @@ -5,7 +5,7 @@ import java.util.UUID; import djmil.cordacheckers.contracts.GameBoardCommand; import djmil.cordacheckers.contracts.UtxoLedgerTransactionUtil; -import djmil.cordacheckers.states.Game.NotInvolved; +import djmil.cordacheckers.states.GameState.NotInvolved; import djmil.cordacheckers.states.GameBoardState; import djmil.cordacheckers.states.Piece; import net.corda.v5.base.types.MemberX500Name; diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java index c8f1252..05c9ae1 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameboard/ListFlow.java @@ -8,7 +8,7 @@ import org.slf4j.LoggerFactory; import djmil.cordacheckers.FlowResult; import djmil.cordacheckers.states.GameBoardState; -import djmil.cordacheckers.states.Game.NotInvolved; +import djmil.cordacheckers.states.GameState.NotInvolved; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; import net.corda.v5.application.flows.CordaInject; diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java index 2f4faa9..3261dcb 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameproposal/CommandFlow.java @@ -10,7 +10,7 @@ import djmil.cordacheckers.contracts.GameProposalCommand; import djmil.cordacheckers.gameboard.GameBoardView; import djmil.cordacheckers.states.GameBoardState; import djmil.cordacheckers.states.GameProposalState; -import djmil.cordacheckers.states.Game.NotInvolved; +import djmil.cordacheckers.states.GameState.NotInvolved; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; import net.corda.v5.application.flows.CordaInject; diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultView.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultView.java index f3fddc5..d106a4b 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultView.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/GameResultView.java @@ -20,9 +20,9 @@ public class GameResultView { } public GameResultView(GameResultState gameResultState) { - this.whitePlayerName = gameResultState.getWhitePlayerName().getCommonName(); - this.blackPlayerName = gameResultState.getBlackPlayerName().getCommonName(); - this.victoryColor = gameResultState.getVictoryColor(); + this.whitePlayerName = gameResultState.getWinnerName().getCommonName(); + this.blackPlayerName = gameResultState.getOpponentName().getCommonName(); + this.victoryColor = gameResultState.getWinnerColor(); this.id = gameResultState.getGameUuid(); }