diff --git a/README.md b/README.md index 86b3c5f3245..0c1464ff026 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,21 @@ # java-chess 체스 게임 구현을 위한 저장소 + +## 기능 요구 사항 + +1단계 + - [x] 명령어를 입력 받는다(start, end) + - [x] (예외) start, end 외의 명령어가 입력된 경우 다시 명령어를 입력 받는다 + - [x] start 를 입력 시 체스판을 초기화 한다 + - [x] end 를 입력 시 게임을 종료한다 + + 2단계 + - [x] 체스 말 규칙에 따라 말을 이동한다 + - [x] 폰은 시작 위치에서 2칸 이동이 가능하고 시작 위치가 아닐 때 대각선의 적을 잡을 수 있다 + - [x] `move source위치 target위치` 명령어로 말을 움직인다 + + 3단계 + - [x] 왕을 잡으면 게임이 종료된다 + - [x] `status` 명령어를 입력하면 현재 남아있는 말의 점수를 구할 수 있다 + - [x] 한 번에 한 쪽의 점수만 계산한다 + - [x] 같은 세로줄에 같은 색의 폰이 있는 경우 1점이 아닌 0.5점을 준다 diff --git a/src/main/java/chess/ChessApplication.java b/src/main/java/chess/ChessApplication.java new file mode 100644 index 00000000000..ac637cc3938 --- /dev/null +++ b/src/main/java/chess/ChessApplication.java @@ -0,0 +1,10 @@ +package chess; + +import chess.controller.ChessController; + +public class ChessApplication { + + public static void main(String[] args) { + ChessController.start(); + } +} \ No newline at end of file diff --git a/src/main/java/chess/controller/ChessController.java b/src/main/java/chess/controller/ChessController.java new file mode 100644 index 00000000000..6cefa41bd88 --- /dev/null +++ b/src/main/java/chess/controller/ChessController.java @@ -0,0 +1,60 @@ +package chess.controller; + +import chess.controller.dto.TeamDto; +import chess.domain.ChessRunner; +import chess.view.ConsoleInputView; +import chess.view.ConsoleOutputView; +import chess.view.InputView; +import chess.view.OutputView; +import org.apache.commons.lang3.StringUtils; + +public class ChessController { + private static InputView inputView = new ConsoleInputView(); + private static OutputView outputView = new ConsoleOutputView(); + + public static void start() { + Command command = getCommand(); + if (command.isStart()) { + ChessRunner chessRunner = new ChessRunner(); + GameController gameController = command.getGameController(); + gameController.execute(chessRunner, StringUtils.EMPTY); + + runChessGame(command, chessRunner); + } + } + + private static Command getCommand() { + try { + return Command.of(inputView.askChessRun()); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + return getCommand(); + } + } + + private static void runChessGame(Command command, ChessRunner chessRunner) { + do { + command = validateExecute(command, chessRunner); + } while (!command.isEnd() && !chessRunner.isEndChess()); + printWinner(chessRunner); + } + + private static Command validateExecute(Command command, ChessRunner chessRunner) { + try { + String commands = inputView.askGameCommand(); + command = Command.of(commands); + GameController gameController = command.getGameController(); + gameController.execute(chessRunner, commands); + } catch (IllegalArgumentException e) { + System.out.println(e.getMessage()); + } + return command; + } + + private static void printWinner(ChessRunner chessRunner) { + if (chessRunner.isEndChess()) { + TeamDto teamDto = new TeamDto(chessRunner.getWinner()); + outputView.printWinner(teamDto.getTeamName()); + } + } +} diff --git a/src/main/java/chess/controller/Command.java b/src/main/java/chess/controller/Command.java new file mode 100644 index 00000000000..c140263d451 --- /dev/null +++ b/src/main/java/chess/controller/Command.java @@ -0,0 +1,37 @@ +package chess.controller; + +import java.util.Arrays; + +public enum Command { + START("start", new StartController()), + MOVE("move", new MoveController()), + STATUS("status", new StatusController()), + END("end", new EndController()); + + private final String command; + private final GameController gameController; + + Command(String command, GameController gameController) { + this.command = command; + this.gameController = gameController; + } + + public static Command of(final String command) { + return Arrays.stream(values()) + .filter(c -> command.contains(c.command)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 명령어를 입력하였습니다.")); + } + + public boolean isStart() { + return this == START; + } + + public GameController getGameController() { + return this.gameController; + } + + public boolean isEnd() { + return this == END; + } +} diff --git a/src/main/java/chess/controller/EndController.java b/src/main/java/chess/controller/EndController.java new file mode 100644 index 00000000000..0beb80032d8 --- /dev/null +++ b/src/main/java/chess/controller/EndController.java @@ -0,0 +1,13 @@ +package chess.controller; + +import chess.domain.ChessRunner; + +public class EndController extends GameController { + public EndController() { + super(); + } + + @Override + public void execute(ChessRunner chessRunner, String input) { + } +} diff --git a/src/main/java/chess/controller/GameController.java b/src/main/java/chess/controller/GameController.java new file mode 100644 index 00000000000..ce494b93043 --- /dev/null +++ b/src/main/java/chess/controller/GameController.java @@ -0,0 +1,26 @@ +package chess.controller; + +import chess.controller.dto.BoardDto; +import chess.controller.dto.PositionDto; +import chess.domain.ChessRunner; +import chess.domain.position.Position; +import chess.view.ConsoleOutputView; +import chess.view.OutputView; + +import java.util.Map; + +public abstract class GameController { + protected final OutputView outputView; + + public GameController() { + this.outputView = new ConsoleOutputView(); + } + + protected void printBoard(final Map board) { + BoardDto boardDto = new BoardDto(board); + PositionDto positionDto = new PositionDto(Position.getPositions()); + this.outputView.printBoard(positionDto.getPositions(), boardDto.get()); + } + + public abstract void execute(ChessRunner chessRunner, String input); +} diff --git a/src/main/java/chess/controller/MoveController.java b/src/main/java/chess/controller/MoveController.java new file mode 100644 index 00000000000..4bc52b69329 --- /dev/null +++ b/src/main/java/chess/controller/MoveController.java @@ -0,0 +1,27 @@ +package chess.controller; + +import chess.domain.ChessRunner; + +public class MoveController extends GameController { + private static final String DELIMITER = " "; + private static final int COMMANDS_SIZE = 2; + private static final int SOURCE_INDEX = 1; + private static final int TARGET_INDEX = 2; + + public MoveController() { + super(); + } + + @Override + public void execute(ChessRunner chessRunner, String input) { + String[] commands = input.split(DELIMITER); + if (commands.length < COMMANDS_SIZE) { + throw new IllegalArgumentException("잘못된 이동 명령어를 입력하였습니다."); + } + + String source = commands[SOURCE_INDEX]; + String target = commands[TARGET_INDEX]; + chessRunner.update(source, target); + printBoard(chessRunner.getBoardEntities()); + } +} diff --git a/src/main/java/chess/controller/StartController.java b/src/main/java/chess/controller/StartController.java new file mode 100644 index 00000000000..d952290efec --- /dev/null +++ b/src/main/java/chess/controller/StartController.java @@ -0,0 +1,14 @@ +package chess.controller; + +import chess.domain.ChessRunner; + +public class StartController extends GameController { + public StartController() { + super(); + } + + @Override + public void execute(ChessRunner chessRunner, String input) { + printBoard(chessRunner.getBoardEntities()); + } +} diff --git a/src/main/java/chess/controller/StatusController.java b/src/main/java/chess/controller/StatusController.java new file mode 100644 index 00000000000..1b950eccbc9 --- /dev/null +++ b/src/main/java/chess/controller/StatusController.java @@ -0,0 +1,19 @@ +package chess.controller; + +import chess.controller.dto.BoardScoreDto; +import chess.controller.dto.TeamDto; +import chess.domain.ChessRunner; + +public class StatusController extends GameController { + public StatusController() { + super(); + } + + @Override + public void execute(ChessRunner chessRunner, String input) { + BoardScoreDto boardScoreDto = new BoardScoreDto(chessRunner.calculateScore()); + TeamDto teamDto = new TeamDto(chessRunner.getCurrentTeam()); + outputView.printStatus(boardScoreDto.getBoardScore(), teamDto.getTeamName()); + printBoard(chessRunner.getBoardEntities()); + } +} diff --git a/src/main/java/chess/controller/dto/BoardDto.java b/src/main/java/chess/controller/dto/BoardDto.java new file mode 100644 index 00000000000..cbc17ea78b5 --- /dev/null +++ b/src/main/java/chess/controller/dto/BoardDto.java @@ -0,0 +1,16 @@ +package chess.controller.dto; + +import java.util.Collections; +import java.util.Map; + +public class BoardDto { + private final Map board; + + public BoardDto(final Map board) { + this.board = board; + } + + public Map get() { + return Collections.unmodifiableMap(this.board); + } +} diff --git a/src/main/java/chess/controller/dto/BoardScoreDto.java b/src/main/java/chess/controller/dto/BoardScoreDto.java new file mode 100644 index 00000000000..d7e457a2076 --- /dev/null +++ b/src/main/java/chess/controller/dto/BoardScoreDto.java @@ -0,0 +1,13 @@ +package chess.controller.dto; + +public class BoardScoreDto { + private final double boardScore; + + public BoardScoreDto(double boardScore) { + this.boardScore = boardScore; + } + + public double getBoardScore() { + return boardScore; + } +} diff --git a/src/main/java/chess/controller/dto/PositionDto.java b/src/main/java/chess/controller/dto/PositionDto.java new file mode 100644 index 00000000000..4083a2f2444 --- /dev/null +++ b/src/main/java/chess/controller/dto/PositionDto.java @@ -0,0 +1,16 @@ +package chess.controller.dto; + +import java.util.Collections; +import java.util.List; + +public class PositionDto { + private final List positions; + + public PositionDto(final List positions) { + this.positions = positions; + } + + public List getPositions() { + return Collections.unmodifiableList(positions); + } +} \ No newline at end of file diff --git a/src/main/java/chess/controller/dto/TeamDto.java b/src/main/java/chess/controller/dto/TeamDto.java new file mode 100644 index 00000000000..073908e98f8 --- /dev/null +++ b/src/main/java/chess/controller/dto/TeamDto.java @@ -0,0 +1,13 @@ +package chess.controller.dto; + +public class TeamDto { + private final String teamName; + + public TeamDto(final String teamName) { + this.teamName = teamName; + } + + public String getTeamName() { + return teamName; + } +} diff --git a/src/main/java/chess/domain/ChessRunner.java b/src/main/java/chess/domain/ChessRunner.java new file mode 100644 index 00000000000..a886c58ebfe --- /dev/null +++ b/src/main/java/chess/domain/ChessRunner.java @@ -0,0 +1,117 @@ +package chess.domain; + +import chess.domain.board.Board; +import chess.domain.board.BoardScore; +import chess.domain.piece.Piece; +import chess.domain.piece.Team; +import chess.domain.position.Position; +import chess.domain.strategy.direction.Direction; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class ChessRunner { + private Board board; + private Team currentTeam; + + public ChessRunner() { + this.board = new Board(); + this.currentTeam = Team.WHITE; + } + + public void update(String source, String target) { + Position sourcePosition = Position.of(source); + Position targetPosition = Position.of(target); + Piece sourcePiece = getSourcePiece(sourcePosition); + + checkCorrectTurn(sourcePiece); + checkUpdateBoard(sourcePosition, targetPosition, sourcePiece); + + updateBoard(sourcePosition, targetPosition); + changeTeam(); + } + + private Piece getSourcePiece(Position source) { + Optional sourcePiece = this.board.getPiece(source); + if (!sourcePiece.isPresent()) { + throw new IllegalArgumentException("비어있는 위치를 선택했습니다."); + } + + return sourcePiece.get(); + } + + private void checkCorrectTurn(Piece sourcePiece) { + if (sourcePiece.isEnemy(this.currentTeam)) { + throw new IllegalArgumentException("현재 차례가 아닙니다."); + } + } + + private void checkUpdateBoard(Position sourcePosition, Position targetPosition, Piece sourcePiece) { + if (isSamePosition(sourcePosition, targetPosition)) { + throw new IllegalArgumentException("같은 위치로 이동할 수 없습니다."); + } + + if (!(sourcePiece.movable(sourcePosition, targetPosition))) { + throw new IllegalArgumentException("선택한 기물이 이동할 수 없는 곳입니다."); + } + + if (!isEmptyPath(sourcePosition, targetPosition)) { + throw new IllegalArgumentException("경로 사이에 장애물이 있습니다."); + } + + if (!isMovableTarget(sourcePiece, targetPosition)) { + throw new IllegalArgumentException("목적지가 잘못되었습니다."); + } + } + + private boolean isSamePosition(final Position sourcePosition, final Position targetPosition) { + return sourcePosition.equals(targetPosition); + } + + private boolean isEmptyPath(final Position sourcePosition, final Position targetPosition) { + Direction direction = Direction.findDirection(sourcePosition, targetPosition); + List path = direction.findPath(sourcePosition, targetPosition); + + if (path.isEmpty()) { + return true; + } + return path.stream() + .allMatch(this.board::isEmpty); + } + + private boolean isMovableTarget(final Piece sourcePiece, final Position targetPosition) { + Optional targetPiece = this.board.getPiece(targetPosition); + return targetPiece.map(sourcePiece::isEnemy).orElse(true); + } + + private void updateBoard(Position sourcePosition, Position targetPosition) { + this.board.updateBoard(sourcePosition, targetPosition); + } + + private void changeTeam() { + this.currentTeam = this.currentTeam.changeTeam(); + } + + public double calculateScore() { + BoardScore boardScore = this.board.calculateScore(this.currentTeam); + return boardScore.getBoardScore(); + } + + public boolean isEndChess() { + return this.board.getWinner().isPresent(); + } + + public Map getBoardEntities() { + return this.board.parse(); + } + + public String getCurrentTeam() { + return this.currentTeam.name(); + } + + public String getWinner() { + Optional winner = this.board.getWinner(); + return winner.map(Enum::name).orElseThrow(AssertionError::new); + } +} diff --git a/src/main/java/chess/domain/board/Board.java b/src/main/java/chess/domain/board/Board.java new file mode 100644 index 00000000000..dd387f3ffb4 --- /dev/null +++ b/src/main/java/chess/domain/board/Board.java @@ -0,0 +1,93 @@ +package chess.domain.board; + +import chess.domain.piece.Piece; +import chess.domain.piece.Team; +import chess.domain.position.File; +import chess.domain.position.Position; + +import java.util.*; +import java.util.stream.Collectors; + +public class Board { + private final Map board; + + public Board() { + this(BoardInitializer.initializeAll()); + } + + public Board(final Map board) { + this.board = board; + } + + public void updateBoard(final Position sourcePosition, final Position targetPosition) { + Piece selectedPiece = this.board.get(sourcePosition); + this.board.put(targetPosition, selectedPiece); + this.board.remove(sourcePosition); + } + + public BoardScore calculateScore(final Team team) { + BoardScore totalScore = calculateTotalScore(team); + return calculatePawnScore(team, totalScore); + } + + private BoardScore calculateTotalScore(final Team team) { + double totalScore = board.values().stream() + .filter(piece -> team.isSameTeamWith(piece.getTeam())) + .mapToDouble(Piece::getScore) + .sum(); + return new BoardScore(totalScore); + } + + private BoardScore calculatePawnScore(final Team team, BoardScore totalScore) { + for (File file : File.values()) { + List> sameFilePawns = this.board.entrySet().stream() + .filter(entry -> File.of(entry.getKey().getFile()).equals(file)) + .filter(entry -> entry.getValue().isPawn() && !entry.getValue().isEnemy(team)) + .collect(Collectors.toList()); + + totalScore = totalScore.pawnStrategy(sameFilePawns); + } + return totalScore; + } + + public Optional getWinner() { + if (checkWhiteKing() && !checkBlackKing()) { + return Optional.of(Team.WHITE); + } + if (!checkWhiteKing() && checkBlackKing()) { + return Optional.of(Team.BLACK); + } + return Optional.empty(); + } + + private boolean checkWhiteKing() { + return this.board.values().stream() + .anyMatch(Piece::isWhiteKing); + } + + private boolean checkBlackKing() { + return this.board.values().stream() + .anyMatch(Piece::isBlackKing); + } + + public boolean isEmpty(final Position position) { + return !this.board.containsKey(position); + } + + public Optional getPiece(final Position position) { + if (this.board.containsKey(position)) { + return Optional.of(this.board.get(position)); + } + return Optional.empty(); + } + + public Map parse() { + Map parseResult = board.entrySet() + .stream() + .collect(Collectors.toMap(entry -> entry.getKey().toString(), + entry -> entry.getValue().toSymbol(), + (e1, e2) -> e1, LinkedHashMap::new)); + + return Collections.unmodifiableMap(parseResult); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/board/BoardInitializer.java b/src/main/java/chess/domain/board/BoardInitializer.java new file mode 100644 index 00000000000..057eb7137b0 --- /dev/null +++ b/src/main/java/chess/domain/board/BoardInitializer.java @@ -0,0 +1,32 @@ +package chess.domain.board; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; +import chess.domain.strategy.initialize.*; + +import java.util.*; + +public class BoardInitializer { + private static final List INITIALIZER; + + static { + INITIALIZER = new ArrayList<>(Arrays.asList( + new KingInitializer(), + new QueenInitializer(), + new RookInitializer(), + new KnightInitializer(), + new BishopInitializer(), + new PawnInitializer() + )); + } + + public static Map initializeAll() { + Map board = new HashMap<>(); + + for (InitializeStrategy strategy : INITIALIZER) { + board.putAll(strategy.initialize()); + } + + return board; + } +} diff --git a/src/main/java/chess/domain/board/BoardScore.java b/src/main/java/chess/domain/board/BoardScore.java new file mode 100644 index 00000000000..fc46c94ffba --- /dev/null +++ b/src/main/java/chess/domain/board/BoardScore.java @@ -0,0 +1,30 @@ +package chess.domain.board; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; + +import java.util.List; +import java.util.Map; + +public class BoardScore { + private static final int PAWN_ON_SAME_FILE = 1; + private static final double PAWN_SCORE_STRATEGY = 0.5; + + private final double boardScore; + + public BoardScore(final double boardScore) { + this.boardScore = boardScore; + } + + public BoardScore pawnStrategy(List> sameFilePawns) { + int pawnsCount = sameFilePawns.size(); + if (pawnsCount > PAWN_ON_SAME_FILE) { + return new BoardScore(this.boardScore - (pawnsCount * PAWN_SCORE_STRATEGY)); + } + return this; + } + + public double getBoardScore() { + return this.boardScore; + } +} diff --git a/src/main/java/chess/domain/piece/Bishop.java b/src/main/java/chess/domain/piece/Bishop.java new file mode 100644 index 00000000000..7151af78465 --- /dev/null +++ b/src/main/java/chess/domain/piece/Bishop.java @@ -0,0 +1,17 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class Bishop extends Piece { + public Bishop(PieceType pieceType, Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(Position source, Position target) { + int fileGap = Math.abs(source.calculateFileGap(target)); + int rankGap = Math.abs(source.calculateRankGap(target)); + + return fileGap == rankGap; + } +} diff --git a/src/main/java/chess/domain/piece/King.java b/src/main/java/chess/domain/piece/King.java new file mode 100644 index 00000000000..d671b45b632 --- /dev/null +++ b/src/main/java/chess/domain/piece/King.java @@ -0,0 +1,21 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class King extends Piece { + private static final int MAX_FILE_GAP = 1; + private static final int MAX_RANK_GAP = 1; + + public King(PieceType pieceType, Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(Position source, Position target) { + int fileGap = Math.abs(source.calculateFileGap(target)); + int rankGap = Math.abs(source.calculateRankGap(target)); + + return fileGap <= MAX_FILE_GAP + && rankGap <= MAX_RANK_GAP; + } +} diff --git a/src/main/java/chess/domain/piece/Knight.java b/src/main/java/chess/domain/piece/Knight.java new file mode 100644 index 00000000000..c7908365708 --- /dev/null +++ b/src/main/java/chess/domain/piece/Knight.java @@ -0,0 +1,23 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class Knight extends Piece { + private static final int HORIZONTAL_FILE_GAP = 2; + private static final int HORIZONTAL_RANK_GAP = 1; + private static final int VERTICAL_FILE_GAP = 1; + private static final int VERTICAL_RANK_GAP = 2; + + public Knight(PieceType pieceType, Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(Position source, Position target) { + int fileGap = Math.abs(source.calculateFileGap(target)); + int rankGap = Math.abs(source.calculateRankGap(target)); + + return (fileGap == HORIZONTAL_FILE_GAP && rankGap == HORIZONTAL_RANK_GAP) + || (fileGap == VERTICAL_FILE_GAP && rankGap == VERTICAL_RANK_GAP); + } +} diff --git a/src/main/java/chess/domain/piece/MoveStrategy.java b/src/main/java/chess/domain/piece/MoveStrategy.java new file mode 100644 index 00000000000..75726bc7ee6 --- /dev/null +++ b/src/main/java/chess/domain/piece/MoveStrategy.java @@ -0,0 +1,7 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public interface MoveStrategy { + boolean movable(Position source, Position target); +} diff --git a/src/main/java/chess/domain/piece/Pawn.java b/src/main/java/chess/domain/piece/Pawn.java new file mode 100644 index 00000000000..6d34deb7351 --- /dev/null +++ b/src/main/java/chess/domain/piece/Pawn.java @@ -0,0 +1,51 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class Pawn extends Piece { + private static final int START_WHITE_RANK = 2; + private static final int START_BLACK_RANK = 7; + private static final int SAME_FILE = 0; + private static final int DIAGONAL = 1; + private static final int UP_WHITE_ONE = -1; + private static final int UP_WHITE_TWO = -2; + private static final int DOWN_BLACK_ONE = 1; + private static final int DOWN_BLACK_TWO = 2; + + public Pawn(final PieceType pieceType, final Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(final Position source, final Position target) { + final int fileGap = source.calculateFileGap(target); + final int rankGap = source.calculateRankGap(target); + + if (this.team.isWhite()) { + return whitePawnMovable(source, fileGap, rankGap); + } + return blackPawnMovable(source, fileGap, rankGap); + } + + private boolean whitePawnMovable(final Position source, final int fileGap, final int rankGap) { + if (source.getRank() == START_WHITE_RANK) { + return fileGap == SAME_FILE + && (rankGap == UP_WHITE_ONE || rankGap == UP_WHITE_TWO); + } + + final int absFileGap = Math.abs(fileGap); + return (absFileGap == SAME_FILE || absFileGap == DIAGONAL) + && rankGap == UP_WHITE_ONE; + } + + private boolean blackPawnMovable(final Position source, final int fileGap, final int rankGap) { + if (source.getRank() == START_BLACK_RANK) { + return fileGap == SAME_FILE + && (rankGap == DOWN_BLACK_ONE || rankGap == DOWN_BLACK_TWO); + } + + final int absFileGap = Math.abs(fileGap); + return (absFileGap == SAME_FILE || absFileGap == DIAGONAL) + && rankGap == DOWN_BLACK_ONE; + } +} diff --git a/src/main/java/chess/domain/piece/Piece.java b/src/main/java/chess/domain/piece/Piece.java new file mode 100644 index 00000000000..a09e9aacb87 --- /dev/null +++ b/src/main/java/chess/domain/piece/Piece.java @@ -0,0 +1,43 @@ +package chess.domain.piece; + +public abstract class Piece implements MoveStrategy { + protected final PieceType pieceType; + protected final Team team; + + public Piece(final PieceType pieceType, final Team team) { + this.pieceType = pieceType; + this.team = team; + } + + public boolean isEnemy(final Piece targetPiece) { + return !this.team.isSameTeamWith(targetPiece.getTeam()); + } + + public boolean isEnemy(final Team team) { + return !this.team.isSameTeamWith(team); + } + + public boolean isWhiteKing() { + return this.pieceType.isKing() && this.team.isWhite(); + } + + public boolean isBlackKing() { + return this.pieceType.isKing() && !this.team.isWhite(); + } + + public boolean isPawn() { + return this.pieceType.isPawn(); + } + + public String toSymbol() { + return this.team.symbolize(this.pieceType.getSymbol()); + } + + public Team getTeam() { + return this.team; + } + + public double getScore() { + return this.pieceType.getScore(); + } +} diff --git a/src/main/java/chess/domain/piece/PieceType.java b/src/main/java/chess/domain/piece/PieceType.java new file mode 100644 index 00000000000..6640ffa3d86 --- /dev/null +++ b/src/main/java/chess/domain/piece/PieceType.java @@ -0,0 +1,34 @@ +package chess.domain.piece; + +public enum PieceType { + KING("k", 0), + QUEEN("q", 9), + ROOK("r", 5), + BISHOP("b", 3), + KNIGHT("n", 2.5), + PAWN("p", 1); + + private final String symbol; + private final double score; + + PieceType(String symbol, double score) { + this.symbol = symbol; + this.score = score; + } + + public boolean isKing() { + return this == KING; + } + + public boolean isPawn() { + return this == PAWN; + } + + public double getScore() { + return score; + } + + public String getSymbol() { + return symbol; + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/piece/Queen.java b/src/main/java/chess/domain/piece/Queen.java new file mode 100644 index 00000000000..ca246843c48 --- /dev/null +++ b/src/main/java/chess/domain/piece/Queen.java @@ -0,0 +1,15 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class Queen extends Piece { + public Queen(PieceType pieceType, Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(Position source, Position target) { + return new Rook(this.pieceType, this.team).movable(source, target) + || new Bishop(this.pieceType, this.team).movable(source, target); + } +} diff --git a/src/main/java/chess/domain/piece/Rook.java b/src/main/java/chess/domain/piece/Rook.java new file mode 100644 index 00000000000..791c12d4c66 --- /dev/null +++ b/src/main/java/chess/domain/piece/Rook.java @@ -0,0 +1,22 @@ +package chess.domain.piece; + +import chess.domain.position.Position; + +public class Rook extends Piece { + + private static final int MIN_FILE_GAP = 0; + private static final int MIN_RANK_GAP = 0; + + public Rook(PieceType pieceType, Team team) { + super(pieceType, team); + } + + @Override + public boolean movable(Position source, Position target) { + int fileGap = Math.abs(source.calculateFileGap(target)); + int rankGap = Math.abs(source.calculateRankGap(target)); + + return (fileGap > MIN_FILE_GAP && rankGap == MIN_RANK_GAP) + || (fileGap == MIN_FILE_GAP && rankGap > MIN_RANK_GAP); + } +} diff --git a/src/main/java/chess/domain/piece/Team.java b/src/main/java/chess/domain/piece/Team.java new file mode 100644 index 00000000000..593352c6ab2 --- /dev/null +++ b/src/main/java/chess/domain/piece/Team.java @@ -0,0 +1,33 @@ +package chess.domain.piece; + +import java.util.function.Function; + +public enum Team { + BLACK(String::toUpperCase), + WHITE(String::toLowerCase); + + private final Function expression; + + Team(Function expression) { + this.expression = expression; + } + + public Team changeTeam() { + if (this == BLACK) { + return WHITE; + } + return BLACK; + } + + public String symbolize(final String name) { + return this.expression.apply(name); + } + + public boolean isSameTeamWith(final Team team) { + return this == team; + } + + public boolean isWhite() { + return this == WHITE; + } +} diff --git a/src/main/java/chess/domain/position/File.java b/src/main/java/chess/domain/position/File.java new file mode 100644 index 00000000000..bb03d1216d0 --- /dev/null +++ b/src/main/java/chess/domain/position/File.java @@ -0,0 +1,40 @@ +package chess.domain.position; + +import java.util.Arrays; + +public enum File { + A('a', 1), + B('b', 2), + C('c', 3), + D('d', 4), + E('e', 5), + F('f', 6), + G('g', 7), + H('h', 8); + + private final char symbol; + private final int number; + + File(char symbol, int number) { + this.symbol = symbol; + this.number = number; + } + + public static File of(final String file) { + return Arrays.stream(values()) + .filter(f -> f.symbol == file.toLowerCase().charAt(0)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 x 좌표값을 입력하였습니다.")); + } + + public static File of(final int file) { + return Arrays.stream(values()) + .filter(f -> f.number == file) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 x 좌표값을 입력하였습니다.")); + } + + public int getNumber() { + return number; + } +} diff --git a/src/main/java/chess/domain/position/Position.java b/src/main/java/chess/domain/position/Position.java new file mode 100644 index 00000000000..f0794eb275e --- /dev/null +++ b/src/main/java/chess/domain/position/Position.java @@ -0,0 +1,96 @@ +package chess.domain.position; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +public class Position { + private static List positions; + + private final File file; + private final Rank rank; + + private Position(final File file, final Rank rank) { + this.file = file; + this.rank = rank; + } + + static { + positions = new ArrayList<>(); + for (Rank rank : Rank.values()) { + addPosition(rank); + } + } + + private static void addPosition(Rank rank) { + for (File file : File.values()) { + positions.add(new Position(file, rank)); + } + } + + public static Position of(final String position) { + File file = File.of(position.substring(0, 1)); + Rank rank = Rank.of(position.substring(1, 2)); + + return findPosition(file, rank); + } + + private static Position findPosition(File file, Rank rank) { + return positions.stream() + .filter(p -> p.file.equals(file) && p.rank.equals(rank)) + .findFirst() + .orElseThrow(AssertionError::new); + } + + public static Position of(final File file, final Rank rank) { + return findPosition(file, rank); + } + + public static Position of(final int fileSymbol, final int rankSymbol) { + return findPosition(File.of(fileSymbol), Rank.of(rankSymbol)); + } + + public int calculateFileGap(final Position target) { + return this.file.compareTo(target.file); + } + + public int calculateRankGap(final Position target) { + return this.rank.getDifference(target.rank); + } + + public static List getPositions() { + List parseResult = positions.stream() + .map(Position::toString) + .collect(Collectors.toList()); + return Collections.unmodifiableList(parseResult); + } + + public int getFile() { + return this.file.getNumber(); + } + + public int getRank() { + return this.rank.getSymbol(); + } + + @Override + public String toString() { + return file.name() + rank.name(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Position position = (Position) o; + return file == position.file && + rank == position.rank; + } + + @Override + public int hashCode() { + return Objects.hash(file, rank); + } +} diff --git a/src/main/java/chess/domain/position/Rank.java b/src/main/java/chess/domain/position/Rank.java new file mode 100644 index 00000000000..002a9c69536 --- /dev/null +++ b/src/main/java/chess/domain/position/Rank.java @@ -0,0 +1,42 @@ +package chess.domain.position; + +import java.util.Arrays; + +public enum Rank { + EIGHT(8), + SEVEN(7), + SIX(6), + FIVE(5), + FOUR(4), + THREE(3), + TWO(2), + ONE(1); + + private final int symbol; + + Rank(int symbol) { + this.symbol = symbol; + } + + public static Rank of(final String rank) { + return Arrays.stream(values()) + .filter(pv -> pv.symbol == Integer.parseInt(rank)) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 y 좌표값을 입력하였습니다.")); + } + + public static Rank of(final int rank) { + return Arrays.stream(values()) + .filter(f -> f.symbol == rank) + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("잘못된 y 좌표값을 입력하였습니다.")); + } + + public int getDifference(final Rank rank) { + return this.symbol - rank.symbol; + } + + public int getSymbol() { + return this.symbol; + } +} diff --git a/src/main/java/chess/domain/strategy/direction/Direction.java b/src/main/java/chess/domain/strategy/direction/Direction.java new file mode 100644 index 00000000000..50aa5d53255 --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/Direction.java @@ -0,0 +1,40 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BiPredicate; + +public enum Direction { + UP(new UpStrategy(), (fileGap, rankGap) -> fileGap == 0 && rankGap > 0), + DOWN(new DownStrategy(), (fileGap, rankGap) -> fileGap == 0 && rankGap < 0), + LEFT(new LeftStrategy(), (fileGap, rankGap) -> fileGap < 0 && rankGap == 0), + RIGHT(new RightStrategy(), (fileGap, rankGap) -> fileGap > 0 && rankGap == 0), + LEFT_UP(new LeftUpStrategy(), (fileGap, rankGap) -> fileGap < 0 && rankGap > 0), + LEFT_DOWN(new LeftDownStrategy(), (fileGap, rankGap) -> fileGap < 0 && rankGap < 0), + RIGHT_UP(new RightUpStrategy(), (fileGap, rankGap) -> fileGap > 0 && rankGap > 0), + RIGHT_DOWN(new RightDownStrategy(), (fileGap, rankGap) -> fileGap > 0 && rankGap < 0); + + private final DirectionStrategy directionStrategy; + private final BiPredicate condition; + + Direction(DirectionStrategy directionStrategy, BiPredicate condition) { + this.directionStrategy = directionStrategy; + this.condition = condition; + } + + public static Direction findDirection(final Position source, final Position target) { + int fileGap = target.calculateFileGap(source); + int rankGap = target.calculateRankGap(source); + + return Arrays.stream(values()) + .filter(direction -> direction.condition.test(fileGap, rankGap)) + .findFirst() + .orElseThrow(AssertionError::new); + } + + public List findPath(final Position source, final Position target) { + return this.directionStrategy.findPath(source, target); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/DirectionStrategy.java b/src/main/java/chess/domain/strategy/direction/DirectionStrategy.java new file mode 100644 index 00000000000..ec27cd66a4a --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/DirectionStrategy.java @@ -0,0 +1,9 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.List; + +public interface DirectionStrategy { + List findPath(Position source, Position target); +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/DownStrategy.java b/src/main/java/chess/domain/strategy/direction/DownStrategy.java new file mode 100644 index 00000000000..4c934153ce7 --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/DownStrategy.java @@ -0,0 +1,19 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class DownStrategy implements DirectionStrategy { + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.range(target.getRank() + 1, source.getRank()) + .mapToObj(index -> Position.of(source.getFile(), index)) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/LeftDownStrategy.java b/src/main/java/chess/domain/strategy/direction/LeftDownStrategy.java new file mode 100644 index 00000000000..9eb5797914a --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/LeftDownStrategy.java @@ -0,0 +1,20 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class LeftDownStrategy implements DirectionStrategy { + + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.range(target.getFile() + 1, source.getFile()) + .mapToObj(index -> Position.of(index, target.getRank() - (target.getFile() - index))) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} diff --git a/src/main/java/chess/domain/strategy/direction/LeftStrategy.java b/src/main/java/chess/domain/strategy/direction/LeftStrategy.java new file mode 100644 index 00000000000..9c03bab43ba --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/LeftStrategy.java @@ -0,0 +1,19 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class LeftStrategy implements DirectionStrategy { + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.range(target.getFile() + 1, source.getFile()) + .mapToObj(index -> Position.of(index, source.getRank())) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/LeftUpStrategy.java b/src/main/java/chess/domain/strategy/direction/LeftUpStrategy.java new file mode 100644 index 00000000000..7be9d112170 --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/LeftUpStrategy.java @@ -0,0 +1,19 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class LeftUpStrategy implements DirectionStrategy { + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.range(target.getFile() + 1, source.getFile()) + .mapToObj(index -> Position.of(index, target.getRank() + (target.getFile() - index))) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/RightDownStrategy.java b/src/main/java/chess/domain/strategy/direction/RightDownStrategy.java new file mode 100644 index 00000000000..6f8988b2d0c --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/RightDownStrategy.java @@ -0,0 +1,20 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class RightDownStrategy implements DirectionStrategy { + + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.rangeClosed(source.getFile() + 1, target.getFile() - 1) + .mapToObj(index -> Position.of(index, target.getRank() + (target.getFile() - index))) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} diff --git a/src/main/java/chess/domain/strategy/direction/RightStrategy.java b/src/main/java/chess/domain/strategy/direction/RightStrategy.java new file mode 100644 index 00000000000..f665648ee0a --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/RightStrategy.java @@ -0,0 +1,19 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class RightStrategy implements DirectionStrategy { + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.rangeClosed(source.getFile() + 1, target.getFile() - 1) + .mapToObj(index -> Position.of(index, source.getRank())) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/direction/RightUpStrategy.java b/src/main/java/chess/domain/strategy/direction/RightUpStrategy.java new file mode 100644 index 00000000000..f321d432ef1 --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/RightUpStrategy.java @@ -0,0 +1,20 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class RightUpStrategy implements DirectionStrategy { + + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.rangeClosed(source.getFile() + 1, target.getFile() - 1) + .mapToObj(index -> Position.of(index, target.getRank() - (target.getFile() - index))) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} diff --git a/src/main/java/chess/domain/strategy/direction/UpStrategy.java b/src/main/java/chess/domain/strategy/direction/UpStrategy.java new file mode 100644 index 00000000000..c309ae1f086 --- /dev/null +++ b/src/main/java/chess/domain/strategy/direction/UpStrategy.java @@ -0,0 +1,19 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public final class UpStrategy implements DirectionStrategy { + @Override + public List findPath(final Position source, final Position target) { + List path = IntStream.rangeClosed(source.getRank() + 1, target.getRank() - 1) + .mapToObj(index -> Position.of(source.getFile(), index)) + .collect(Collectors.toList()); + + return Collections.unmodifiableList(path); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/initialize/BishopInitializer.java b/src/main/java/chess/domain/strategy/initialize/BishopInitializer.java new file mode 100644 index 00000000000..de5017b6892 --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/BishopInitializer.java @@ -0,0 +1,42 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Bishop; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class BishopInitializer implements InitializeStrategy { + private enum InitialBishop { + BLACK_LEFT(Position.of("c8"), new Bishop(PieceType.BISHOP, Team.BLACK)), + BLACK_RIGHT(Position.of("f8"), new Bishop(PieceType.BISHOP, Team.BLACK)), + WHITE_LEFT(Position.of("c1"), new Bishop(PieceType.BISHOP, Team.WHITE)), + WHITE_RIGHT(Position.of("f1"), new Bishop(PieceType.BISHOP, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialBishop(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initializeBishops() { + Map bishops = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(bishops); + } + } + + @Override + public Map initialize() { + return InitialBishop.initializeBishops(); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/initialize/InitializeStrategy.java b/src/main/java/chess/domain/strategy/initialize/InitializeStrategy.java new file mode 100644 index 00000000000..18ae42a7b9d --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/InitializeStrategy.java @@ -0,0 +1,10 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Piece; +import chess.domain.position.Position; + +import java.util.Map; + +public interface InitializeStrategy { + Map initialize(); +} diff --git a/src/main/java/chess/domain/strategy/initialize/KingInitializer.java b/src/main/java/chess/domain/strategy/initialize/KingInitializer.java new file mode 100644 index 00000000000..caec8420870 --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/KingInitializer.java @@ -0,0 +1,40 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.King; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class KingInitializer implements InitializeStrategy { + private enum InitialKing { + BLACK_KING(Position.of("e8"), new King(PieceType.KING, Team.BLACK)), + WHITE_KING(Position.of("e1"), new King(PieceType.KING, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialKing(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initializeKings() { + Map kings = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(kings); + } + } + + @Override + public Map initialize() { + return InitialKing.initializeKings(); + } +} diff --git a/src/main/java/chess/domain/strategy/initialize/KnightInitializer.java b/src/main/java/chess/domain/strategy/initialize/KnightInitializer.java new file mode 100644 index 00000000000..fa95bb0823d --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/KnightInitializer.java @@ -0,0 +1,42 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Knight; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class KnightInitializer implements InitializeStrategy { + private enum InitialKnight { + BLACK_LEFT(Position.of("b8"), new Knight(PieceType.KNIGHT, Team.BLACK)), + BLACK_RIGHT(Position.of("g8"), new Knight(PieceType.KNIGHT, Team.BLACK)), + WHITE_LEFT(Position.of("b1"), new Knight(PieceType.KNIGHT, Team.WHITE)), + WHITE_RIGHT(Position.of("g1"), new Knight(PieceType.KNIGHT, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialKnight(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initializeKnights() { + Map knights = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(knights); + } + } + + @Override + public Map initialize() { + return InitialKnight.initializeKnights(); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/initialize/PawnInitializer.java b/src/main/java/chess/domain/strategy/initialize/PawnInitializer.java new file mode 100644 index 00000000000..523acea9c0a --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/PawnInitializer.java @@ -0,0 +1,55 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class PawnInitializer implements InitializeStrategy { + private enum InitialPawn { + BLACK_A7(Position.of("a7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_B7(Position.of("b7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_C7(Position.of("c7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_D7(Position.of("d7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_E7(Position.of("e7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_F7(Position.of("f7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_G7(Position.of("g7"), new Pawn(PieceType.PAWN, Team.BLACK)), + BLACK_H7(Position.of("h7"), new Pawn(PieceType.PAWN, Team.BLACK)), + + WHITE_A2(Position.of("a2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_B2(Position.of("b2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_C2(Position.of("c2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_D2(Position.of("d2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_E2(Position.of("e2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_F2(Position.of("f2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_G2(Position.of("g2"), new Pawn(PieceType.PAWN, Team.WHITE)), + WHITE_H2(Position.of("h2"), new Pawn(PieceType.PAWN, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialPawn(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initialPawns() { + Map pawns = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(pawns); + } + } + + @Override + public Map initialize() { + return InitialPawn.initialPawns(); + } +} diff --git a/src/main/java/chess/domain/strategy/initialize/QueenInitializer.java b/src/main/java/chess/domain/strategy/initialize/QueenInitializer.java new file mode 100644 index 00000000000..e23871b6bd9 --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/QueenInitializer.java @@ -0,0 +1,40 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Queen; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class QueenInitializer implements InitializeStrategy { + private enum InitialQueen { + BLACK_QUEEN(Position.of("d8"), new Queen(PieceType.QUEEN, Team.BLACK)), + WHITE_QUEEN(Position.of("d1"), new Queen(PieceType.QUEEN, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialQueen(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initializeQueens() { + Map queens = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(queens); + } + } + + @Override + public Map initialize() { + return InitialQueen.initializeQueens(); + } +} \ No newline at end of file diff --git a/src/main/java/chess/domain/strategy/initialize/RookInitializer.java b/src/main/java/chess/domain/strategy/initialize/RookInitializer.java new file mode 100644 index 00000000000..8b351c3088d --- /dev/null +++ b/src/main/java/chess/domain/strategy/initialize/RookInitializer.java @@ -0,0 +1,42 @@ +package chess.domain.strategy.initialize; + +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Rook; +import chess.domain.piece.Team; +import chess.domain.position.Position; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Collectors; + +public final class RookInitializer implements InitializeStrategy { + private enum InitialRook { + BLACK_LEFT(Position.of("a8"), new Rook(PieceType.ROOK, Team.BLACK)), + BLACK_RIGHT(Position.of("h8"), new Rook(PieceType.ROOK, Team.BLACK)), + WHITE_LEFT(Position.of("a1"), new Rook(PieceType.ROOK, Team.WHITE)), + WHITE_RIGHT(Position.of("h1"), new Rook(PieceType.ROOK, Team.WHITE)); + + private final Position position; + private final Piece piece; + + InitialRook(Position position, Piece piece) { + this.position = position; + this.piece = piece; + } + + public static Map initializeRooks() { + Map rooks = Arrays.stream(values()) + .collect(Collectors.toMap(entry -> entry.position, entry -> entry.piece, + (e1, e2) -> e1, HashMap::new)); + return Collections.unmodifiableMap(rooks); + } + } + + @Override + public Map initialize() { + return InitialRook.initializeRooks(); + } +} \ No newline at end of file diff --git a/src/main/java/chess/view/ConsoleInputView.java b/src/main/java/chess/view/ConsoleInputView.java new file mode 100644 index 00000000000..0649abe99fb --- /dev/null +++ b/src/main/java/chess/view/ConsoleInputView.java @@ -0,0 +1,21 @@ +package chess.view; + +import java.util.Scanner; + +public class ConsoleInputView implements InputView { + private static final Scanner SCANNER = new Scanner(System.in); + + @Override + public String askChessRun() { + System.out.println("> 체스 게임을 시작합니다."); + System.out.println("> 게임 시작 : start"); + System.out.println("> 게임 종료 : end"); + System.out.println("> 게임 이동 : move source위치 target위치 - 예. move b2 b3"); + return SCANNER.nextLine(); + } + + @Override + public String askGameCommand() { + return SCANNER.nextLine(); + } +} diff --git a/src/main/java/chess/view/ConsoleOutputView.java b/src/main/java/chess/view/ConsoleOutputView.java new file mode 100644 index 00000000000..b85b029b013 --- /dev/null +++ b/src/main/java/chess/view/ConsoleOutputView.java @@ -0,0 +1,42 @@ +package chess.view; + +import java.util.List; +import java.util.Map; + +public class ConsoleOutputView implements OutputView { + private static final String EMPTY_SQUARE = "."; + private static final int MAX_BOARD_SIZE = 8; + + @Override + public void printBoard(final List positions, final Map board) { + for (int i = 1; i <= positions.size(); i++) { + String piece = board.get(positions.get(i - 1)); + System.out.print(printPiece(piece)); + checkNewLine(i); + } + System.out.println(); + } + + private void checkNewLine(final int i) { + if (i % MAX_BOARD_SIZE == 0) { + System.out.println(); + } + } + + private String printPiece(String piece) { + if (piece == null) { + return EMPTY_SQUARE; + } + return piece; + } + + @Override + public void printStatus(double calculateScore, String teamName) { + System.out.println(String.format("%s 팀의 점수는 %.1f 점입니다.", teamName, calculateScore)); + } + + @Override + public void printWinner(String teamName) { + System.out.println(String.format("%s 팀이 이겼습니다.", teamName)); + } +} diff --git a/src/main/java/chess/view/InputView.java b/src/main/java/chess/view/InputView.java new file mode 100644 index 00000000000..d3a8f06a520 --- /dev/null +++ b/src/main/java/chess/view/InputView.java @@ -0,0 +1,7 @@ +package chess.view; + +public interface InputView { + String askChessRun(); + + String askGameCommand(); +} diff --git a/src/main/java/chess/view/OutputView.java b/src/main/java/chess/view/OutputView.java new file mode 100644 index 00000000000..155d5eaca09 --- /dev/null +++ b/src/main/java/chess/view/OutputView.java @@ -0,0 +1,12 @@ +package chess.view; + +import java.util.List; +import java.util.Map; + +public interface OutputView { + void printBoard(List positions, Map board); + + void printStatus(double calculateScore, String teamName); + + void printWinner(String teamName); +} diff --git a/src/test/java/chess/domain/ChessRunnerTest.java b/src/test/java/chess/domain/ChessRunnerTest.java new file mode 100644 index 00000000000..2bc1178c4c6 --- /dev/null +++ b/src/test/java/chess/domain/ChessRunnerTest.java @@ -0,0 +1,77 @@ +package chess.domain; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class ChessRunnerTest { + ChessRunner chessRunner; + + @BeforeEach + void setUp() { + chessRunner = new ChessRunner(); + } + + @DisplayName("현재 순서가 아닐 때 에러 메시지 출력") + @Test + void validateCurrentTeam() { + assertThatThrownBy(() -> chessRunner.update("a7", "a5")) //블랙 팀 폰 이동 + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("현재 차례가 아닙니다."); + } + + @DisplayName("같은 위치로 이동할 때 에러 메시지 출력") + @Test + void validateSamePosition() { + assertThatThrownBy(() -> chessRunner.update("a2", "a2")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("같은 위치로 이동할 수 없습니다."); + } + + @DisplayName("선택한 기물에 맞지 않는 목적지를 선택했을 때 에레 메시지 출력") + @Test + void validateMovable() { + assertThatThrownBy(() -> chessRunner.update("a1", "b2")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("선택한 기물이 이동할 수 없는 곳입니다."); + } + + @DisplayName("경로 사이에 장애물이 있을 때 에러 메시지 출력") + @Test + void validateObstacle() { + assertThatThrownBy(() -> chessRunner.update("a1", "a3")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("경로 사이에 장애물이 있습니다."); + } + + @DisplayName("잘못된 목적지를 선택했을 때 에러 메시지 출력") + @Test + void validateTarget() { + assertThatThrownBy(() -> chessRunner.update("a1", "a2")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("목적지가 잘못되었습니다."); + } + + @DisplayName("점수 계산 테스트") + @Test + void calculateScoreTest() { + assertThat(chessRunner.calculateScore()).isEqualTo(38d); + } + + @DisplayName("게임이 종료되었는지 검사") + @Test + void isEndChessTest() { + assertThat(chessRunner.isEndChess()).isFalse(); + } + + @DisplayName("게임이 종료되지 않았을 때 승자를 출력 시 예외 출력") + @Test + void getWinnerTest() { + assertThatThrownBy(() -> { + chessRunner.getWinner(); + }).isInstanceOf(AssertionError.class); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/board/BoardScoreTest.java b/src/test/java/chess/domain/board/BoardScoreTest.java new file mode 100644 index 00000000000..f7528351431 --- /dev/null +++ b/src/test/java/chess/domain/board/BoardScoreTest.java @@ -0,0 +1,28 @@ +package chess.domain.board; + +import chess.domain.piece.Pawn; +import chess.domain.piece.Piece; +import chess.domain.piece.PieceType; +import chess.domain.piece.Team; +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.*; + +public class BoardScoreTest { + @DisplayName("같은 파일에 폰이 있을 때 점수 계산") + @Test + void pawnStrategyTest() { + List> sameFilePawns = new ArrayList<>(Arrays.asList( + new AbstractMap.SimpleEntry<>(Position.of("a3"), new Pawn(PieceType.PAWN, Team.WHITE)), + new AbstractMap.SimpleEntry<>(Position.of("a4"), new Pawn(PieceType.PAWN, Team.WHITE)), + new AbstractMap.SimpleEntry<>(Position.of("a5"), new Pawn(PieceType.PAWN, Team.WHITE)) + )); + + BoardScore boardScore = new BoardScore(10d); + + Assertions.assertThat(boardScore.pawnStrategy(sameFilePawns).getBoardScore()).isEqualTo(8.5); + } +} diff --git a/src/test/java/chess/domain/board/BoardTest.java b/src/test/java/chess/domain/board/BoardTest.java new file mode 100644 index 00000000000..9e9bfc6cf6c --- /dev/null +++ b/src/test/java/chess/domain/board/BoardTest.java @@ -0,0 +1,112 @@ +package chess.domain.board; + +import chess.domain.piece.*; +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; + +public class BoardTest { + @DisplayName("빈 칸으로 말 이동") + @Test + void updateAtEmptyBoard() { + Board board = new Board(); + Position source = Position.of("a2"); + Position target = Position.of("a3"); + board.updateBoard(source, target); + + Assertions.assertThatThrownBy(() -> { + board.getPiece(source).get(); + }).isInstanceOf(NoSuchElementException.class); + Assertions.assertThat(board.getPiece(target).get()).isInstanceOf(Piece.class); + } + + @DisplayName("상대 편으로 말 이동") + @Test + void updateAtEnemy() { + Piece rook = new Rook(PieceType.ROOK, Team.WHITE); + Piece pawn = new Pawn(PieceType.PAWN, Team.BLACK); + Map entry = new HashMap<>(); + Position source = Position.of("a2"); + Position target = Position.of("a3"); + entry.put(source, rook); + entry.put(target, pawn); + Board board = new Board(entry); + board.updateBoard(source, target); + + Assertions.assertThatThrownBy(() -> { + board.getPiece(source).get(); + }).isInstanceOf(NoSuchElementException.class); + Assertions.assertThat(board.getPiece(target).get()).isInstanceOf(Piece.class); + } + + @DisplayName("승자 팀을 판단") + @Test + void checkWinnerTest() { + Map whiteKing = new HashMap<>(); + whiteKing.put(Position.of("a3"), new King(PieceType.KING, Team.WHITE)); + Board whiteWinBoard = new Board(whiteKing); + + Map blackKing = new HashMap<>(); + blackKing.put(Position.of("b4"), new King(PieceType.KING, Team.BLACK)); + Board blackWinBoard = new Board(blackKing); + + Map bothKing = new HashMap<>(); + bothKing.put(Position.of("a5"), new King(PieceType.KING, Team.BLACK)); + bothKing.put(Position.of("b7"), new King(PieceType.KING, Team.WHITE)); + Board drawBoard = new Board(bothKing); + + Assertions.assertThat(whiteWinBoard.getWinner().get()).isEqualTo(Team.WHITE); + Assertions.assertThat(blackWinBoard.getWinner().get()).isEqualTo(Team.BLACK); + Assertions.assertThatThrownBy(() -> { + drawBoard.getWinner().get(); + }).isInstanceOf(NoSuchElementException.class); + } + + @DisplayName("지정한 칸이 비어있는지 판단") + @Test + void isEmptyTest() { + Position nonEmptyTarget = Position.of("a2"); + Position emptyTarget = Position.of("a3"); + Board board = new Board(); + + Assertions.assertThat(board.isEmpty(nonEmptyTarget)).isFalse(); + Assertions.assertThat(board.isEmpty(emptyTarget)).isTrue(); + } + + @DisplayName("지정한 칸의 체스 말 반환") + @Test + void getPieceTest() { + Board board = new Board(); + + Assertions.assertThat(board.getPiece(Position.of("a2")).get()).isInstanceOf(Piece.class); + Assertions.assertThatThrownBy(() -> { + board.getPiece(Position.of("a3")).get(); + }).isInstanceOf(NoSuchElementException.class); + } + + @DisplayName("같은 세로줄에 폰이 없을 때 점수 계산") + @Test + void calculateScoreWithoutSameFilePawnTest() { + Board board = new Board(); + + Assertions.assertThat(board.calculateScore(Team.WHITE).getBoardScore()).isEqualTo(38d); + Assertions.assertThat(board.calculateScore(Team.BLACK).getBoardScore()).isEqualTo(38d); + } + + @DisplayName("같은 세로줄에 폰이 있을 때 점수 계산") + @Test + void calculateScoreWithSameFilePawn() { + Map sameFilePawn = new HashMap<>(); + sameFilePawn.put(Position.of("a3"), new Pawn(PieceType.PAWN, Team.WHITE)); + sameFilePawn.put(Position.of("a4"), new Pawn(PieceType.PAWN, Team.WHITE)); + sameFilePawn.put(Position.of("a5"), new Pawn(PieceType.PAWN, Team.WHITE)); + Board board = new Board(sameFilePawn); + + Assertions.assertThat(board.calculateScore(Team.WHITE).getBoardScore()).isEqualTo(1.5d); + } +} diff --git a/src/test/java/chess/domain/piece/BishopTest.java b/src/test/java/chess/domain/piece/BishopTest.java new file mode 100644 index 00000000000..e063194bd80 --- /dev/null +++ b/src/test/java/chess/domain/piece/BishopTest.java @@ -0,0 +1,20 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class BishopTest { + @DisplayName("비숍이 이동 가능한 곳인지 검사") + @Test + void movableTest() { + Piece bishop = new Bishop(PieceType.BISHOP, Team.WHITE); + Position source = Position.of("d4"); + Position movableTarget = Position.of("f6"); + Position nonMovableTarget = Position.of("d5"); + + Assertions.assertThat(bishop.movable(source, movableTarget)).isTrue(); + Assertions.assertThat(bishop.movable(source, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/KingTest.java b/src/test/java/chess/domain/piece/KingTest.java new file mode 100644 index 00000000000..f621c911470 --- /dev/null +++ b/src/test/java/chess/domain/piece/KingTest.java @@ -0,0 +1,20 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class KingTest { + @DisplayName("킹이 이동 가능한 곳인지 검사") + @Test + void movableTest() { + Piece king = new King(PieceType.KING, Team.BLACK); + Position source = Position.of("d4"); + Position movableTarget = Position.of("d5"); + Position nonMovableTarget = Position.of("c7"); + + Assertions.assertThat(king.movable(source, movableTarget)).isTrue(); + Assertions.assertThat(king.movable(source, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/KnightTest.java b/src/test/java/chess/domain/piece/KnightTest.java new file mode 100644 index 00000000000..24ac2b7bd85 --- /dev/null +++ b/src/test/java/chess/domain/piece/KnightTest.java @@ -0,0 +1,20 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class KnightTest { + @DisplayName("나이트가 이동 가능한 곳인지 검사") + @Test + void movableTest() { + Piece knight = new Knight(PieceType.KNIGHT, Team.BLACK); + Position source = Position.of("d4"); + Position movableTarget = Position.of("c6"); + Position nonMovableTarget = Position.of("c7"); + + Assertions.assertThat(knight.movable(source, movableTarget)).isTrue(); + Assertions.assertThat(knight.movable(source, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/PawnTest.java b/src/test/java/chess/domain/piece/PawnTest.java new file mode 100644 index 00000000000..42222b383f4 --- /dev/null +++ b/src/test/java/chess/domain/piece/PawnTest.java @@ -0,0 +1,54 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PawnTest { + @DisplayName("흰색 폰이 이동 가능한 곳인지 검사") + @Test + void whitePawnMovableTest() { + Piece whitePawn = new Pawn(PieceType.PAWN, Team.WHITE); + Position initialSource = Position.of("a2"); + Position directOne = Position.of("a3"); + Position directTwo = Position.of("a4"); + Position nonMovableInitial = Position.of("a5"); + + Position nonInitialSource = Position.of("b3"); + Position diagonalTarget = Position.of("c4"); + Position directTarget = Position.of("b4"); + Position nonMovableTarget = Position.of("b5"); + + Assertions.assertThat(whitePawn.movable(initialSource, directOne)).isTrue(); + Assertions.assertThat(whitePawn.movable(initialSource, directTwo)).isTrue(); + Assertions.assertThat(whitePawn.movable(initialSource, nonMovableInitial)).isFalse(); + + Assertions.assertThat(whitePawn.movable(nonInitialSource, diagonalTarget)).isTrue(); + Assertions.assertThat(whitePawn.movable(nonInitialSource, directTarget)).isTrue(); + Assertions.assertThat(whitePawn.movable(nonInitialSource, nonMovableTarget)).isFalse(); + } + + @DisplayName("검은색 폰이 이동 가능한 곳인지 검사") + @Test + void blackPawnMovableTest() { + Piece blackPawn = new Pawn(PieceType.PAWN, Team.BLACK); + Position initialSource = Position.of("a7"); + Position directOne = Position.of("a6"); + Position directTwo = Position.of("a5"); + Position nonMovableInitial = Position.of("a4"); + + Position nonInitialSource = Position.of("b6"); + Position diagonalTarget = Position.of("c5"); + Position directTarget = Position.of("b5"); + Position nonMovableTarget = Position.of("b4"); + + Assertions.assertThat(blackPawn.movable(initialSource, directOne)).isTrue(); + Assertions.assertThat(blackPawn.movable(initialSource, directTwo)).isTrue(); + Assertions.assertThat(blackPawn.movable(initialSource, nonMovableInitial)).isFalse(); + + Assertions.assertThat(blackPawn.movable(nonInitialSource, diagonalTarget)).isTrue(); + Assertions.assertThat(blackPawn.movable(nonInitialSource, directTarget)).isTrue(); + Assertions.assertThat(blackPawn.movable(nonInitialSource, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/PieceTest.java b/src/test/java/chess/domain/piece/PieceTest.java new file mode 100644 index 00000000000..5b5268a57b0 --- /dev/null +++ b/src/test/java/chess/domain/piece/PieceTest.java @@ -0,0 +1,57 @@ +package chess.domain.piece; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class PieceTest { + @DisplayName("팀 별 대소문자로 출력") + @Test + void toSymbol() { + Piece whitePiece = new Bishop(PieceType.BISHOP, Team.WHITE); + Piece blackPiece = new Bishop(PieceType.BISHOP, Team.BLACK); + + assertThat(whitePiece.toSymbol()).isEqualTo("b"); + assertThat(blackPiece.toSymbol()).isEqualTo("B"); + } + + @DisplayName("피스의 팀 구분") + @Test + void isEnemy() { + Piece whitePiece = new Knight(PieceType.KNIGHT, Team.WHITE); + Piece blackPiece = new Knight(PieceType.KNIGHT, Team.BLACK); + + assertThat(whitePiece.isEnemy(blackPiece)).isTrue(); + assertThat(whitePiece.isEnemy(Team.BLACK)).isTrue(); + assertThat(blackPiece.isEnemy(Team.WHITE)).isTrue(); + } + + @DisplayName("피스가 흰 팀의 킹인지 테스트") + @Test + void isWhiteKing() { + Piece whiteKing = new King(PieceType.KING, Team.WHITE); + Piece blackBishop = new Bishop(PieceType.BISHOP, Team.BLACK); + + assertThat(whiteKing.isWhiteKing()).isTrue(); + assertThat(blackBishop.isWhiteKing()).isFalse(); + } + + @DisplayName("피스가 검은 팀의 킹인지 테스트") + @Test + void isBlackKing() { + Piece blackKing = new King(PieceType.KING, Team.BLACK); + Piece whiteBishop = new Bishop(PieceType.BISHOP, Team.WHITE); + assertThat(blackKing.isBlackKing()).isTrue(); + assertThat(whiteBishop.isBlackKing()).isFalse(); + } + + @DisplayName("피스가 폰인지 테스트") + @Test + void isPawn() { + Piece whitePawn = new Pawn(PieceType.PAWN, Team.WHITE); + Piece blackQueen = new Queen(PieceType.QUEEN, Team.BLACK); + assertThat(whitePawn.isPawn()).isTrue(); + assertThat(blackQueen.isPawn()).isFalse(); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/piece/QueenTest.java b/src/test/java/chess/domain/piece/QueenTest.java new file mode 100644 index 00000000000..59a70e90cc1 --- /dev/null +++ b/src/test/java/chess/domain/piece/QueenTest.java @@ -0,0 +1,22 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class QueenTest { + @DisplayName("퀸이 이동 가능한 곳인지 검사") + @Test + void movableTest() { + Piece queen = new Queen(PieceType.QUEEN, Team.BLACK); + Position source = Position.of("d4"); + Position diagonalTarget = Position.of("g7"); + Position directTarget = Position.of("d8"); + Position nonMovableTarget = Position.of("c7"); + + Assertions.assertThat(queen.movable(source, diagonalTarget)).isTrue(); + Assertions.assertThat(queen.movable(source, directTarget)).isTrue(); + Assertions.assertThat(queen.movable(source, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/RookTest.java b/src/test/java/chess/domain/piece/RookTest.java new file mode 100644 index 00000000000..7826eecfbee --- /dev/null +++ b/src/test/java/chess/domain/piece/RookTest.java @@ -0,0 +1,20 @@ +package chess.domain.piece; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class RookTest { + @DisplayName("룩이 이동 가능한 곳인지 검사") + @Test + void movableTest() { + Piece rook = new Rook(PieceType.ROOK, Team.BLACK); + Position source = Position.of("d4"); + Position movableTarget = Position.of("d7"); + Position nonMovableTarget = Position.of("c7"); + + Assertions.assertThat(rook.movable(source, movableTarget)).isTrue(); + Assertions.assertThat(rook.movable(source, nonMovableTarget)).isFalse(); + } +} diff --git a/src/test/java/chess/domain/piece/TeamTest.java b/src/test/java/chess/domain/piece/TeamTest.java new file mode 100644 index 00000000000..e792ec8643a --- /dev/null +++ b/src/test/java/chess/domain/piece/TeamTest.java @@ -0,0 +1,20 @@ +package chess.domain.piece; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class TeamTest { + @DisplayName("팀 순서 변경 테스트") + @Test + void changeTeam() { + Team blackTeam = Team.BLACK; + Team whiteTeam = Team.WHITE; + + Team blackExpected = blackTeam.changeTeam(); + Team whiteExpected = whiteTeam.changeTeam(); + + Assertions.assertThat(blackExpected).isEqualTo(Team.WHITE); + Assertions.assertThat(whiteExpected).isEqualTo(Team.BLACK); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/position/FileTest.java b/src/test/java/chess/domain/position/FileTest.java new file mode 100644 index 00000000000..d5892185db6 --- /dev/null +++ b/src/test/java/chess/domain/position/FileTest.java @@ -0,0 +1,15 @@ +package chess.domain.position; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class FileTest { + @DisplayName("잘못된 x 좌표값이 들어왔을 때 예외 출력") + @Test + void ofTest() { + Assertions.assertThatThrownBy(() -> { + File.of("i"); + }).isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/position/PositionTest.java b/src/test/java/chess/domain/position/PositionTest.java new file mode 100644 index 00000000000..890c88097cf --- /dev/null +++ b/src/test/java/chess/domain/position/PositionTest.java @@ -0,0 +1,15 @@ +package chess.domain.position; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +public class PositionTest { + @DisplayName("입력한 값으로 position 변경하는 기능") + @Test + void moveTest() { + Position position = Position.of("b2"); + + Assertions.assertThat(position).isEqualTo(Position.of("b2")); + } +} diff --git a/src/test/java/chess/domain/position/RankTest.java b/src/test/java/chess/domain/position/RankTest.java new file mode 100644 index 00000000000..485e04d7753 --- /dev/null +++ b/src/test/java/chess/domain/position/RankTest.java @@ -0,0 +1,15 @@ +package chess.domain.position; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +class RankTest { + @DisplayName("잘못된 y 좌표 입력값에 대해 예외 처리 테스트") + @Test + void ofTest() { + Assertions.assertThatThrownBy(() -> { + Rank.of("9"); + }).isInstanceOf(IllegalArgumentException.class); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/strategy/direction/DirectionTest.java b/src/test/java/chess/domain/strategy/direction/DirectionTest.java new file mode 100644 index 00000000000..9c160a57849 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/DirectionTest.java @@ -0,0 +1,73 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DirectionTest { + @DisplayName("위 방향 이동") + @Test + void checkUpStrategy() { + Position source = Position.of("a1"); + Position target = Position.of("a8"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.UP); + } + + @DisplayName("아래 방향 이동") + @Test + void checkDownStrategy() { + Position source = Position.of("a8"); + Position target = Position.of("a1"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.DOWN); + } + + @DisplayName("왼쪽 방향 이동") + @Test + void checkLeftStrategy() { + Position source = Position.of("e5"); + Position target = Position.of("b5"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.LEFT); + } + + @DisplayName("오른쪽 방향 이동") + @Test + void checkRightStrategy() { + Position source = Position.of("b5"); + Position target = Position.of("e5"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.RIGHT); + } + + @DisplayName("왼쪽 위 방향 이동") + @Test + void checkLeftUpStrategy() { + Position source = Position.of("e1"); + Position target = Position.of("c3"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.LEFT_UP); + } + + @DisplayName("왼쪽 아래 방향 이동") + @Test + void checkLeftDownStrategy() { + Position source = Position.of("h8"); + Position target = Position.of("a1"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.LEFT_DOWN); + } + + @DisplayName("오른쪽 위 방향 이동") + @Test + void checkRightUpStrategy() { + Position source = Position.of("a1"); + Position target = Position.of("h8"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.RIGHT_UP); + } + + @DisplayName("오른쪽 아래 방향 이동") + @Test + void checkRightDownStrategy() { + Position source = Position.of("a8"); + Position target = Position.of("e1"); + assertThat(Direction.findDirection(source, target)).isEqualTo(Direction.RIGHT_DOWN); + } +} \ No newline at end of file diff --git a/src/test/java/chess/domain/strategy/direction/DownStrategyTest.java b/src/test/java/chess/domain/strategy/direction/DownStrategyTest.java new file mode 100644 index 00000000000..95c10219872 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/DownStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class DownStrategyTest { + @DisplayName("하단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new DownStrategy(); + Position source = Position.of("a7"); + Position target = Position.of("a2"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("a3"), + Position.of("a4"), + Position.of("a5"), + Position.of("a6") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/LeftDownStrategyTest.java b/src/test/java/chess/domain/strategy/direction/LeftDownStrategyTest.java new file mode 100644 index 00000000000..662a64c0ed8 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/LeftDownStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class LeftDownStrategyTest { + @DisplayName("좌측 하단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new LeftDownStrategy(); + Position source = Position.of("g7"); + Position target = Position.of("b2"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("c3"), + Position.of("d4"), + Position.of("e5"), + Position.of("f6") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/LeftStrategyTest.java b/src/test/java/chess/domain/strategy/direction/LeftStrategyTest.java new file mode 100644 index 00000000000..a8c126e60c2 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/LeftStrategyTest.java @@ -0,0 +1,27 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class LeftStrategyTest { + @DisplayName("좌측 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new LeftStrategy(); + Position source = Position.of("g2"); + Position target = Position.of("a2"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("b2"), + Position.of("c2"), + Position.of("d2"), + Position.of("e2"), + Position.of("f2") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/LeftUpStrategyTest.java b/src/test/java/chess/domain/strategy/direction/LeftUpStrategyTest.java new file mode 100644 index 00000000000..22689bd2bfc --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/LeftUpStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class LeftUpStrategyTest { + @DisplayName("좌측 상단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new LeftUpStrategy(); + Position source = Position.of("g2"); + Position target = Position.of("b7"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("c6"), + Position.of("d5"), + Position.of("e4"), + Position.of("f3") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/RightDownStrategyTest.java b/src/test/java/chess/domain/strategy/direction/RightDownStrategyTest.java new file mode 100644 index 00000000000..38d160091d5 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/RightDownStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class RightDownStrategyTest { + @DisplayName("우측 하단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new RightDownStrategy(); + Position source = Position.of("b7"); + Position target = Position.of("g2"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("c6"), + Position.of("d5"), + Position.of("e4"), + Position.of("f3") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/RightStrategyTest.java b/src/test/java/chess/domain/strategy/direction/RightStrategyTest.java new file mode 100644 index 00000000000..b77f11878ec --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/RightStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class RightStrategyTest { + @DisplayName("우측 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new RightStrategy(); + Position source = Position.of("b2"); + Position target = Position.of("g2"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("c2"), + Position.of("d2"), + Position.of("e2"), + Position.of("f2") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/RightUpStrategyTest.java b/src/test/java/chess/domain/strategy/direction/RightUpStrategyTest.java new file mode 100644 index 00000000000..616ed9fbb94 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/RightUpStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class RightUpStrategyTest { + @DisplayName("우측 상단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new RightUpStrategy(); + Position source = Position.of("b2"); + Position target = Position.of("g7"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("c3"), + Position.of("d4"), + Position.of("e5"), + Position.of("f6") + ); + } +} diff --git a/src/test/java/chess/domain/strategy/direction/UpStrategyTest.java b/src/test/java/chess/domain/strategy/direction/UpStrategyTest.java new file mode 100644 index 00000000000..4999a795810 --- /dev/null +++ b/src/test/java/chess/domain/strategy/direction/UpStrategyTest.java @@ -0,0 +1,26 @@ +package chess.domain.strategy.direction; + +import chess.domain.position.Position; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class UpStrategyTest { + @DisplayName("상단 이동 시 경로 구하기") + @Test + void findPathTest() { + DirectionStrategy directionStrategy = new UpStrategy(); + Position source = Position.of("b2"); + Position target = Position.of("b7"); + List path = directionStrategy.findPath(source, target); + + Assertions.assertThat(path).containsExactly( + Position.of("b3"), + Position.of("b4"), + Position.of("b5"), + Position.of("b6") + ); + } +}