diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java index df420a9..1d7c5ee 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/CordaClient.java @@ -67,7 +67,7 @@ public class CordaClient { } // use custodian holding identity to get ranking table of all players - public List fetchRanking(HoldingIdentity holdingIdentity) { + public Map fetchRanking(HoldingIdentity holdingIdentity) { final RequestBody requestBody = new RequestBody( "ranking-" +UUID.randomUUID(), "djmil.cordacheckers.gameresult.RankingFlow", diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/Rank.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/Rank.java index 3dda122..8005ffd 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/Rank.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/Rank.java @@ -1,7 +1,6 @@ package djmil.cordacheckers.cordaclient.dao; public record Rank( - String name, Integer gamesPlayed, Integer gamesWon ) { diff --git a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/RspRankList.java b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/RspRankList.java index 78a3c10..3adb703 100644 --- a/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/RspRankList.java +++ b/backend/src/main/java/djmil/cordacheckers/cordaclient/dao/flow/arguments/RspRankList.java @@ -1,11 +1,11 @@ package djmil.cordacheckers.cordaclient.dao.flow.arguments; -import java.util.List; +import java.util.Map; import djmil.cordacheckers.cordaclient.dao.Rank; public record RspRankList( - List successStatus, - String failureStatus) implements Rsp> { + Map successStatus, + String failureStatus) implements Rsp> { } diff --git a/backend/src/test/java/djmil/cordacheckers/cordaclient/GameProposalTests.java b/backend/src/test/java/djmil/cordacheckers/cordaclient/GameProposalTests.java index cc2096f..d363802 100644 --- a/backend/src/test/java/djmil/cordacheckers/cordaclient/GameProposalTests.java +++ b/backend/src/test/java/djmil/cordacheckers/cordaclient/GameProposalTests.java @@ -144,7 +144,7 @@ public class GameProposalTests { assertThat(acceptedGameAcquierView.opponentName()).isEqualToIgnoringCase(issuer); assertThat(acceptedGameAcquierView.opponentColor()).isEqualByComparingTo(acquierColor.opposite()); assertThat(acceptedGameAcquierView.board()).containsAllEntriesOf(GameState.defaultGameBoard); - assertThat(acceptedGameAcquierView.previousMove()).isEmpty(); + assertThat(acceptedGameAcquierView.previousMove()).isNull(); assertThat(acceptedGameAcquierView.moveNumber() == 0); assertThat(acceptedGameAcquierView.status()).isEqualByComparingTo(Status.GAME_BOARD_WAIT_FOR_YOU); assertThat(acceptedGameAcquierView.uuid()).isEqualByComparingTo(game.uuid()); @@ -160,7 +160,7 @@ public class GameProposalTests { assertThat(acceptedGameIssuerView.opponentName()).isEqualToIgnoringCase(acquier); assertThat(acceptedGameIssuerView.opponentColor()).isEqualByComparingTo(acquierColor); assertThat(acceptedGameIssuerView.board()).containsAllEntriesOf(GameState.defaultGameBoard); - assertThat(acceptedGameIssuerView.previousMove()).isEmpty(); + assertThat(acceptedGameIssuerView.previousMove()).isNull(); assertThat(acceptedGameIssuerView.moveNumber() == 0); assertThat(acceptedGameIssuerView.status()).isEqualByComparingTo(Status.GAME_BOARD_WAIT_FOR_OPPONENT); assertThat(acceptedGameIssuerView.uuid()).isEqualByComparingTo(game.uuid()); diff --git a/backend/src/test/java/djmil/cordacheckers/cordaclient/RankingTests.java b/backend/src/test/java/djmil/cordacheckers/cordaclient/RankingTests.java index f0db6d9..b376a7e 100644 --- a/backend/src/test/java/djmil/cordacheckers/cordaclient/RankingTests.java +++ b/backend/src/test/java/djmil/cordacheckers/cordaclient/RankingTests.java @@ -1,8 +1,12 @@ package djmil.cordacheckers.cordaclient; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; -import java.util.List; +import java.util.Arrays; +import java.util.Map; + +import javax.naming.InvalidNameException; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -24,33 +28,77 @@ public class RankingTests { final String player1 = "kumar"; final String player2 = "bobik"; + final static Stone WHITE_MAN = new Stone(Stone.Color.WHITE, Stone.Type.MAN); + final static Stone WHITE_KING = new Stone(Stone.Color.WHITE, Stone.Type.KING); + final static Stone BLACK_MAN = new Stone(Stone.Color.BLACK, Stone.Type.MAN); + final static Stone BLACK_KING = new Stone(Stone.Color.BLACK, Stone.Type.KING); + + final static Map board1 = Map.ofEntries( + Map.entry(10, BLACK_KING), + Map.entry(7, WHITE_KING) + ); + @Test - void testGlobalRanking() { + void testSurrender() throws InvalidNameException { final var hiCustodian = holdingIdentityResolver.getCustodian(); assertThat(hiCustodian).isNotNull(); - final List liderboard1 = cordaClient.fetchRanking(hiCustodian); + final Map liderboard1 = cordaClient.fetchRanking(hiCustodian); final var hiWinner = holdingIdentityResolver.getByUsername(player1); final var hiLooser = holdingIdentityResolver.getByUsername(player2); + final var winnerName = hiWinner.getName(); + final var losserName = hiLooser.getName(); - final GameState game = cordaClient.gameProposalCreate( - hiWinner, hiLooser, Stone.Color.WHITE, "GameBoard GLOBAL_RANKING test"); + final GameState game = cordaClient.gameProposalCreate(hiWinner, hiLooser, Stone.Color.WHITE, + "GameBoard GLOBAL_RANKING surrender test"); cordaClient.gameProposalAccept(hiLooser, game.uuid()); cordaClient.gameBoardSurrender(hiLooser, game.uuid()); - final List liderboard2 = cordaClient.fetchRanking(hiCustodian); + final Map liderboard2 = cordaClient.fetchRanking(hiCustodian); - System.out.println(liderboard1); - System.out.println(liderboard2); + assertThat(liderboard1.get(winnerName).gamesWon() +1 == liderboard2.get(winnerName).gamesWon() ); + assertThat(liderboard1.get(winnerName).gamesPlayed() +1 == liderboard2.get(winnerName).gamesPlayed()); + + assertThat(liderboard1.get(losserName).gamesWon() == liderboard2.get(losserName).gamesWon() ); + assertThat(liderboard1.get(losserName).gamesPlayed() +1 == liderboard2.get(losserName).gamesPlayed()); + } + + @Test + void testVictory() throws InvalidNameException { + final var hiCustodian = holdingIdentityResolver.getCustodian(); + assertThat(hiCustodian).isNotNull(); + + final Map liderboard1 = cordaClient.fetchRanking(hiCustodian); + + final var hiWinner = holdingIdentityResolver.getByUsername(player2); + final var hiLooser = holdingIdentityResolver.getByUsername(player1); + final var winnerName = hiWinner.getName(); + final var losserName = hiLooser.getName(); + + final GameState game = cordaClient.gameProposalCreate(hiWinner, hiLooser, Stone.Color.BLACK, + board1, "GameBoard GLOBAL_RANKING victory test"); + + cordaClient.gameProposalAccept(hiLooser, game.uuid()); + cordaClient.gameBoardMove(hiWinner, game.uuid(), Arrays.asList(7, 14), null); + + final Map liderboard2 = cordaClient.fetchRanking(hiCustodian); + + assertThat(liderboard1.get(winnerName).gamesWon() +1 == liderboard2.get(winnerName).gamesWon() ); + assertThat(liderboard1.get(winnerName).gamesPlayed() +1 == liderboard2.get(winnerName).gamesPlayed()); + + assertThat(liderboard1.get(losserName).gamesWon() == liderboard2.get(losserName).gamesWon() ); + assertThat(liderboard1.get(losserName).gamesPlayed() +1 == liderboard2.get(losserName).gamesPlayed()); } @Test void testIndividualRanking() { final var hiCustodian = holdingIdentityResolver.getByUsername(player2); - final List liderboard = cordaClient.fetchRanking(hiCustodian); - System.out.println(liderboard); + + assertThatNoException().isThrownBy( () -> + cordaClient.fetchRanking(hiCustodian) + ); } } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/VNode.java b/corda/workflows/src/main/java/djmil/cordacheckers/VNode.java index d5014d7..a6203a4 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/VNode.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/VNode.java @@ -8,20 +8,30 @@ import net.corda.v5.membership.NotaryInfo; public class VNode { static final String CHECKERS = "Checkers"; // O aka Organization + static final String PLAYER = "Player"; // OU aka OrganizationUnit static final String CUSTODIAN = "Custodian"; // OU aka OrganizationUnit static final String NOTARY = "Notary"; // OU aka OrganizationUnit @CordaInject MemberLookup memberLookup; + public static boolean isCordaCherckersPlayer(MemberInfo memberInfo) { + final MemberX500Name memberName = memberInfo.getName(); + + return memberName.getOrganization().equals(CHECKERS) && + memberName.getOrganizationUnit().equals(PLAYER); + } + public static boolean isCordaCherckersCustodian(MemberInfo memberInfo) { final MemberX500Name memberName = memberInfo.getName(); + return memberName.getOrganization().equals(CHECKERS) && memberName.getOrganizationUnit().equals(CUSTODIAN); } public static boolean isCordaCherckersNotary(NotaryInfo notaryInfo) { final MemberX500Name memberName = notaryInfo.getName(); + return memberName.getOrganization().equals(CHECKERS) && memberName.getOrganizationUnit().equals(NOTARY); } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/Rank.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/Rank.java index dc427b6..1f1017f 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/Rank.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/Rank.java @@ -1,20 +1,36 @@ package djmil.cordacheckers.gameresult; public class Rank { - public final String name; - public final Integer gamesPlayed; - public final Integer gamesWon; + private Integer gamesPlayed; + private Integer gamesWon; // Serialisation service requires a default constructor - Rank() { - name = null; - gamesPlayed = null; - gamesWon = null; + public Rank() { + gamesPlayed = 0; + gamesWon = 0; } - public Rank(String name, Integer gamesPlayed, Integer gamesWon) { - this.name = name; + public Rank(Integer gamesPlayed, Integer gamesWon) { this.gamesPlayed = gamesPlayed; this.gamesWon = gamesWon; } + + Rank gamePlayed() { + gamesPlayed++; + return this; + } + + Rank gameWon() { + gamesWon++; + return this; + } + + public Integer getGamesPlayed() { + return gamesPlayed; + } + + public Integer getGamesWon() { + return gamesWon; + } + } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlow.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlow.java index 6ba1559..6cee272 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlow.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlow.java @@ -1,7 +1,6 @@ package djmil.cordacheckers.gameresult; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -9,12 +8,13 @@ import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import djmil.cordacheckers.VNode; import djmil.cordacheckers.states.GameResultState; import net.corda.v5.application.flows.ClientRequestBody; import net.corda.v5.application.flows.ClientStartableFlow; import net.corda.v5.application.flows.CordaInject; -import net.corda.v5.application.flows.FlowEngine; import net.corda.v5.application.marshalling.JsonMarshallingService; +import net.corda.v5.application.membership.MemberLookup; import net.corda.v5.base.annotations.Suspendable; import net.corda.v5.ledger.utxo.UtxoLedgerService; @@ -29,7 +29,7 @@ public class RankingFlow implements ClientStartableFlow { public JsonMarshallingService jsonMarshallingService; @CordaInject - public FlowEngine flowEngine; + public MemberLookup memberLookup; @Suspendable @Override @@ -37,27 +37,23 @@ public class RankingFlow implements ClientStartableFlow { try { final List gameStateResutList = getGameResultsList(); - Map won = new HashMap<>(); + Map leaderboard = newLeaderboard(); for (GameResultState gs : gameStateResutList) { final var winner = gs.getWinnerName().getCommonName(); - won.put(winner, won.getOrDefault(winner, 0) +1); - } + if (winner != null) + leaderboard.put(winner, leaderboard.get(winner).gameWon() ); + + final var blackPlayer = gs.getBlackPlayer().getCommonName(); + leaderboard.put(blackPlayer, leaderboard.get(blackPlayer).gamePlayed() ); - Map played = new HashMap<>(won); - for (GameResultState gs : gameStateResutList) { - final var looseer = gs.getLooserName().getCommonName(); - played.put(looseer, played.getOrDefault(looseer, 0) +1); - } - - List board = new LinkedList(); - for (String name : played.keySet()) { - board.add(new Rank(name, played.getOrDefault(name, 0), won.getOrDefault(name, 0))); + final var whitePlayer = gs.getWhitePlayer().getCommonName(); + leaderboard.put(whitePlayer, leaderboard.get(whitePlayer).gamePlayed() ); } - return new RankingFlowResponce(board) + return new RankingFlowResponce(leaderboard) .toJsonEncodedString(jsonMarshallingService); } catch (Exception e) { - log.warn("Exception during processing " + requestBody + " request: " + e.getMessage()); + log.warn(requestBody + " [ERROR] " +e.toString()); return new RankingFlowResponce(e) .toJsonEncodedString(jsonMarshallingService); } @@ -72,4 +68,16 @@ public class RankingFlow implements ClientStartableFlow { .collect(Collectors.toList()); } + @Suspendable + Map newLeaderboard() { + Map leaderboard = new HashMap<>(); + + memberLookup.lookup() + .stream() + .filter(member -> VNode.isCordaCherckersPlayer(member) ) + .forEach(member -> leaderboard.put(member.getName().getCommonName(), new Rank()) ); + + return leaderboard; + } + } diff --git a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlowResponce.java b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlowResponce.java index 2dcc36b..e67635e 100644 --- a/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlowResponce.java +++ b/corda/workflows/src/main/java/djmil/cordacheckers/gameresult/RankingFlowResponce.java @@ -1,14 +1,14 @@ package djmil.cordacheckers.gameresult; -import java.util.List; +import java.util.Map; import net.corda.v5.application.marshalling.JsonMarshallingService; public class RankingFlowResponce { - public final List successStatus; + public final Map successStatus; public final String failureStatus; - public RankingFlowResponce(List success) { + public RankingFlowResponce(Map success) { this.successStatus = success; this.failureStatus = null; } @@ -21,5 +21,5 @@ public class RankingFlowResponce { public String toJsonEncodedString(JsonMarshallingService jsonMarshallingService) { return jsonMarshallingService.format(this); } - -} \ No newline at end of file + +}