Skip to content

Commit

Permalink
sw_concepts: replace code examples slides with code examples in code
Browse files Browse the repository at this point in the history
Only seeing part of the example was difficult.
And the slides did not offer enough space
to show a meaningful part of an example.
So now the students can browse a normal maven repo and look
for the patterns they learned.
  • Loading branch information
BacLuc committed May 17, 2024
1 parent 465ab95 commit b653352
Show file tree
Hide file tree
Showing 57 changed files with 3,004 additions and 411 deletions.
1 change: 0 additions & 1 deletion topics/sw_concepts/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
include(js_document)

js_slides(sw_concept_slides sw_concept_slides.md)
js_slides(sw_concept_code_examples sw_concept_code_examples.md)

file(GLOB_RECURSE code RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "code/*")
js_add_to_global_archive_file_list(${code})
12 changes: 12 additions & 0 deletions topics/sw_concepts/code/pattern-examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
</properties>

<dependencies>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.24.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
Expand All @@ -35,6 +41,12 @@
<version>5.10.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>5.11.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package ch.scs.jumpstart.pattern.examples.checkers;

import ch.scs.jumpstart.pattern.examples.checkers.dom.*;
import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.MoveValidator;
import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.NoOtherMoveToJumpPossible;
import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
import java.util.List;
import java.util.Optional;

@SuppressWarnings("ClassCanBeRecord")
public class GameLogic {
private final Console console;
private final Board board;
private final List<MoveValidator> moveValidators;
private final MoveExecutor moveExecutor;
private final NoOtherMoveToJumpPossible noOtherMoveToJumpPossible;
private final WinCondition winCondition;

public GameLogic(
Console console,
Board board,
List<MoveValidator> moveValidators,
MoveExecutor moveExecutor,
NoOtherMoveToJumpPossible noOtherMoveToJumpPossible,
WinCondition winCondition) {
this.console = console;
this.board = board;
this.moveValidators = moveValidators;
this.moveExecutor = moveExecutor;
this.noOtherMoveToJumpPossible = noOtherMoveToJumpPossible;
this.winCondition = winCondition;
}

public void run() {
Player currentPlayer = Player.PLAYER_RED;
while (true) {
Player otherPlayer =
currentPlayer == Player.PLAYER_RED ? Player.PLAYER_WHITE : Player.PLAYER_RED;
console.print(
currentPlayer
+ ", make your move. Or type 'undo' to go back to the start of the turn of "
+ otherPlayer);
if (doPlayerMove(currentPlayer)) {
return;
}
if (currentPlayer == Player.PLAYER_WHITE) {
currentPlayer = Player.PLAYER_RED;
} else {
currentPlayer = Player.PLAYER_WHITE;
}
}
}

@SuppressWarnings({"PMD.CyclomaticComplexity", "PMD.CognitiveComplexity"})
private boolean doPlayerMove(Player player) {
BoardCoordinates startCoordinatesForMultipleJump = null;
while (true) {
String userInput = console.getUserInput();
if ("undo".equals(userInput.toLowerCase().trim())) {
try {
Player playerOfUndoneMove = board.undoLastTurn();
if (playerOfUndoneMove.equals(player)) {
continue;
} else {
return false;
}
} catch (Board.NoPreviousMovesException e) {
console.print("There were no previous moves to undo. Please make a move.");
continue;
}
}
Move move;
try {
move = Move.parse(player, userInput);
} catch (IllegalArgumentException e) {
console.print("Invalid input, try again");
continue;
}
if (startCoordinatesForMultipleJump != null
&& !startCoordinatesForMultipleJump.equals(move.start())) {
console.print("For a multiple jump move, the same piece has to be used. Try again");
continue;
}
Optional<Piece> pieceAtStart = board.getPieceAt(move.start());
if (!moveValidators.stream().allMatch(moveValidator -> moveValidator.validate(move, board))) {
console.print("Invalid move, try again");
continue;
}

Move executedMove = moveExecutor.executeMove(move);

boolean hasPlayerWon = winCondition.hasPlayerWon(player, board);
if (hasPlayerWon) {
console.print("Congratulations, player " + player + " has won");
return true;
}

if (executedMove.jumpGambleResult() == JumpGambleResult.WON) {
console.print("The gamble has been won, " + player + " can play again.");
console.print(
player + ", make your move. Or type 'undo' to go back to the start of your turn.");
continue;
}

Optional<Piece> pieceAtEnd = board.getPieceAt(move.end());
boolean wasKingCreated = wasKingCreated(pieceAtStart.orElse(null), pieceAtEnd.orElse(null));
if (wasKingCreated) {
return false;
}
if (!move.isJumpMove()
|| !noOtherMoveToJumpPossible.jumpMovePossibleFrom(move.end(), board)) {
return false;
}
console.print("Multiple jump move for " + player + ". Enter your next jump.");
console.print("Or type 'undo' to go back to the start of your turn.");
startCoordinatesForMultipleJump = move.end();
}
}

private boolean wasKingCreated(Piece pieceAtStart, Piece pieceAtEnd) {
if (pieceAtStart == null || pieceAtEnd == null) {
return false;
}
return !pieceAtStart.isKing() && pieceAtEnd.isKing();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package ch.scs.jumpstart.pattern.examples.checkers;

import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.*;
import ch.scs.jumpstart.pattern.examples.checkers.util.BoardPrinter;
import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
import ch.scs.jumpstart.pattern.examples.checkers.util.Console;
import ch.scs.jumpstart.pattern.examples.checkers.util.PointsCalculator;
import java.util.List;

public class Main {
public static void main(String[] args) {
GameLogic gameLogic =
createGameLogic(Console.getInstance(), new CoinTosser(new PointsCalculator()));

gameLogic.run();
}

public static GameLogic createGameLogic(Console console, CoinTosser coinTosser) {
Board board = new Board();
BoardPrinter boardPrinter = new BoardPrinter(console);
board.registerObserver(boardPrinter::printBoard);

MoveIsDiagonal moveIsDiagonal = new MoveIsDiagonal();
MoveLength moveLength = new MoveLength();
MoveIsForwardIfNotKing moveIsForwardIfNotKing = new MoveIsForwardIfNotKing();
OpponentPieceBetweenJump opponentPieceBetweenJump = new OpponentPieceBetweenJump();
TargetFieldEmpty targetFieldEmpty = new TargetFieldEmpty();
NoOtherMoveToJumpPossible noOtherMoveToJumpPossible =
new NoOtherMoveToJumpPossible(
moveIsForwardIfNotKing, opponentPieceBetweenJump, targetFieldEmpty);
StartPieceValid startPieceValid = new StartPieceValid();

List<MoveValidator> moveValidators =
List.of(
moveIsDiagonal,
moveIsForwardIfNotKing,
moveLength,
noOtherMoveToJumpPossible,
opponentPieceBetweenJump,
startPieceValid,
targetFieldEmpty);

WinCondition winCondition =
new WinCondition(
List.of(
moveIsDiagonal,
moveLength,
moveIsForwardIfNotKing,
opponentPieceBetweenJump,
targetFieldEmpty,
startPieceValid));

MoveExecutor moveExecutor = new MoveExecutor(board, coinTosser, console);
GameLogic gameLogic =
new GameLogic(
console, board, moveValidators, moveExecutor, noOtherMoveToJumpPossible, winCondition);

console.print("Welcome to checkers");
boardPrinter.printBoard(board);
return gameLogic;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package ch.scs.jumpstart.pattern.examples.checkers;

import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.HEADS;
import static ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser.Result.TAILS;

import ch.scs.jumpstart.pattern.examples.checkers.dom.JumpGambleResult;
import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
import ch.scs.jumpstart.pattern.examples.checkers.util.CoinTosser;
import ch.scs.jumpstart.pattern.examples.checkers.util.Console;

@SuppressWarnings("ClassCanBeRecord")
public class MoveExecutor {
private final Board board;
private final CoinTosser coinTosser;
private final Console console;

public MoveExecutor(Board board, CoinTosser coinTosser, Console console) {
this.board = board;
this.coinTosser = coinTosser;
this.console = console;
}

public Move executeMove(Move move) {
if (!move.isJumpMove()) {
board.executeMove(move);
return move;
}
console.print(move.player() + " is making a jump move. Now " + move.player() + " may gamble.");
console.print("If " + move.player() + " does not gamble, the jump move is executed normally.");
console.print("If " + move.player() + " does gamble, a coin will be tossed.");
console.print(
"If the coin toss is "
+ HEADS
+ ", then the jump move will be executed, but "
+ move.player()
+ " gets another turn.");
console.print(
"If the coin toss is "
+ TAILS
+ ", then the jump move fails and the piece at "
+ move.start()
+ ", which would have been used for the jump move, is removed.");
GambleChoice gambleChoice = null;
do {
console.print(
move.player() + " type \"yes\" to gamble, \"no\" to execute the jump move normally");
String userInput = console.getUserInput();
try {
gambleChoice = GambleChoice.valueOf(userInput.trim().toUpperCase());
} catch (IllegalArgumentException e) {
console.print("The input of " + move.player() + " was invalid. Input was: " + userInput);
}
} while (gambleChoice == null);
if (gambleChoice == GambleChoice.NO) {
board.executeMove(move);
return move;
}
CoinTosser.Result tossResult = coinTosser.toss(board, move.player());
JumpGambleResult jumpGambleResult =
tossResult == HEADS ? JumpGambleResult.WON : JumpGambleResult.LOST;
console.print("Coin toss resulted in " + tossResult + ", the gamble was: " + jumpGambleResult);
Move newMove = move.withJumpGambleResult(jumpGambleResult);
board.executeMove(newMove);
return newMove;
}

private enum GambleChoice {
@SuppressWarnings("unused")
YES,
NO
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Checkers pattern examples

These examples were taken from [Soco21-group8 checkersV3](https://github.com/soco21/soco21-group8/tree/main/assignment-3/checkersv3)
with the permission of it's author [@BacLuc](https://github.com/BacLuc)
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ch.scs.jumpstart.pattern.examples.checkers;

import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates;
import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Column;
import ch.scs.jumpstart.pattern.examples.checkers.dom.BoardCoordinates.Row;
import ch.scs.jumpstart.pattern.examples.checkers.dom.Move;
import ch.scs.jumpstart.pattern.examples.checkers.dom.Piece;
import ch.scs.jumpstart.pattern.examples.checkers.dom.Player;
import ch.scs.jumpstart.pattern.examples.checkers.dom.board.Board;
import ch.scs.jumpstart.pattern.examples.checkers.movevalidator.MoveValidator;
import java.util.List;
import java.util.Optional;

/**
* Check if one player cannot move or has no pieces. This can be done in one go, as if one player
* has no pieces, he cannot move. Use MoveValidators to check if any move is possible.
*/
@SuppressWarnings("ClassCanBeRecord")
public class WinCondition {
private final List<MoveValidator> moveValidators;

public WinCondition(List<MoveValidator> moveValidators) {
this.moveValidators = moveValidators;
}

public boolean hasPlayerWon(Player player, Board board) {
for (Row row : Row.values()) {
for (Column col : Column.values()) {
BoardCoordinates currentCoordinates = new BoardCoordinates(row, col);
Optional<Piece> pieceAt = board.getPieceAt(currentCoordinates);
if (pieceAt.isEmpty()) {
continue;
}
Piece piece = pieceAt.get();
if (piece.owner() == player) {
continue;
}
List<Move> possibleMoves =
Move.generatePossibleMoves(currentCoordinates, piece.owner(), List.of(1, 2));
if (possibleMoves.stream()
.anyMatch(
move ->
moveValidators.stream()
.allMatch(moveValidator -> moveValidator.validate(move, board)))) {
return false;
}
}
}
return true;
}
}
Loading

0 comments on commit b653352

Please sign in to comment.