Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,46 @@
## 우아한테크코스 코드리뷰

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)


# 체스
체스 기물의 움직임을 구현한다.

## view
- [x] 체스 판을 출력할 수 있다.

## domain
### 체스 판
- [x] 행은 1~8, 열은 a~h로 표현된다.(8x8)
- [x] 체스 판을 초기화할 수 있다.
- [x] 특정 위치의 말을 원하는 위치로 움직일 수 있다.

### 체스 기물

- [x] 출발지와 도착지를 기반으로, 각 기물이 해당 경로로 갈 수 있는지 스스로 확인할 수 있다.
- [x] 경로에 기물이 존재하는 경우 움직일 수 없다.(knight 제외)
- [x] 도착지에 상대 기물이 존재하는 경우 잡을 수 있다.
- [x] 본인의 색깔이 무엇인지 알고 있다.
- [x] 본인이 잡혔는지 알고 있다.

#### king
- [x] 상, 하, 좌, 우, 대각선으로 1칸 이동할 수 있다.
- [x] 잡히면 게임이 종료된다.

#### bishop
- [x] 대각선으로 여러 칸 이동할 수 있다.

#### knight
- [x] 상, 하, 좌, 우로 1칸 이동 후 대각선으로 1칸 이동할 수 있다.
- [x] 다른 기물을 뛰어넘을 수 있다.

#### Pawn
- [x] 앞으로 1칸 이동할 수 있다.
- [x] 초기 위치에서는 앞으로 2칸까지 이동할 수 있다.
- [x] 대각선 한 칸 앞에 위치한 기물을 잡을 수 있다.

#### Queen
- [x] 수직, 수평, 대각선으로 여러 칸 이동할 수 있다.

#### Rook
- [x] 수직, 수평으로 여러 칸 이동할 수 있다.
14 changes: 14 additions & 0 deletions src/main/java/chess/Application.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package chess;

import chess.controller.ChessController;
import chess.view.InputView;
import chess.view.OutputView;

public class Application {

public static void main(String[] args) {

ChessController chessController = new ChessController(new InputView(), new OutputView());
chessController.run();
}
}
55 changes: 55 additions & 0 deletions src/main/java/chess/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package chess.controller;

import chess.domain.board.ChessBoard;
import chess.domain.Color;
import chess.domain.Position;
import chess.domain.board.ChessBoardBasicInitializer;
import chess.view.InputView;
import chess.view.OutputView;
import java.util.List;

public class ChessController {

private final InputView inputView;
private final OutputView outputView;

public ChessController(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
}

public void run() {
ChessBoard chessBoard = new ChessBoard(new ChessBoardBasicInitializer());
Color nowTurn = Color.WHITE;

while (true) {
outputView.printChessBoard(chessBoard);
outputView.printTurnMessage(nowTurn);

processChessTurn(chessBoard, nowTurn);
if (chessBoard.checkOppositeKingCaptured(nowTurn)) {
break;
}
nowTurn = nowTurn.opposite();
}

outputView.printWinningMessage(nowTurn);
}

private void processChessTurn(ChessBoard chessBoard, Color nowTurn) {
InputProcessor.processUntilSuccess(() -> {
List<Position> originAndDestination = inputView.getMoveInput();
Position origin = originAndDestination.get(0);
checkValidPieceColor(chessBoard, nowTurn, origin);
Position destination = originAndDestination.get(1);

chessBoard.moveAndCapturePiece(origin, destination);
}, OutputView::printErrorMessage);
}

private void checkValidPieceColor(ChessBoard chessBoard, Color nowTurn, Position origin) {
if (chessBoard.getPieceOfPosition(origin).getColor() != nowTurn) {
throw new IllegalArgumentException("현재는 %s의 차례입니다.".formatted(nowTurn.name()));
}
}
}
38 changes: 38 additions & 0 deletions src/main/java/chess/controller/InputProcessor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package chess.controller;

import java.util.function.Consumer;
import java.util.function.Supplier;

public class InputProcessor {

public static void processUntilSuccess(Runnable runnable, Consumer<String> printer) {
while(true) {
try {
runnable.run();
return;
} catch(Exception e) {
printer.accept(e.getMessage());
}
}
}

public static <T> void processUntilSuccess(Consumer<T> consumer, Consumer<String> printer, T t) {
while(true) {
try {
consumer.accept(t);
} catch(Exception e) {
printer.accept(e.getMessage());
}
}
}

public static <T> T processUntilSuccess(Supplier<T> supplier, Consumer<String> printer) {
while(true) {
try {
return supplier.get();
} catch(Exception e) {
printer.accept(e.getMessage());
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package chess;
package chess.domain;

import java.util.List;

public enum Color {

Expand All @@ -25,4 +27,8 @@ public Color opposite() {
default -> EMPTY;
};
}

public static List<Color> getGameColors() {
return List.of(WHITE, BLACK);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chess;
package chess.domain;

public enum Column {

Expand All @@ -15,6 +15,7 @@ public boolean isFarLeft() {
return ordinal() == 0;
}


public boolean isFarRight() {
return ordinal() + 1 == values().length;
}
Expand All @@ -32,7 +33,7 @@ public Column moveLeft(final int step) {
return values()[ordinal() - step];
}

throw new IllegalStateException("움직일 수 없는 위치입니다.");
throw new IllegalStateException("움직일 수 없는 위치입니다. column left");
}

public boolean canMoveRight(final int step) {
Expand All @@ -48,6 +49,6 @@ public Column moveRight(final int step) {
return values()[ordinal() + step];
}

throw new IllegalStateException("움직일 수 없는 위치입니다.");
throw new IllegalStateException("움직일 수 없는 위치입니다. column right");
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chess;
package chess.domain;

public enum Movement {
UP(0, 1),
Expand Down Expand Up @@ -45,4 +45,8 @@ public boolean isVertical() {
public boolean isDiagonal() {
return x != 0 && y != 0 && Math.abs(x) == Math.abs(y);
}

public boolean isTwoTimeVerticalMove() {
return this == UP_UP || this == DOWN_DOWN;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chess;
package chess.domain;

public record Position(
Column column,
Expand Down Expand Up @@ -167,4 +167,12 @@ public Position moveHorizontal(final int step) {
}
return this;
}

@Override
public String toString() {
return "Position{" +
"column=" + column +
", row=" + row +
'}';
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chess;
package chess.domain;

public enum Row {

Expand Down Expand Up @@ -32,7 +32,7 @@ public Row moveUp(final int step) {
return values()[ordinal() - step];
}

throw new IllegalStateException("움직일 수 없는 위치입니다.");
throw new IllegalStateException("움직일 수 없는 위치입니다. row up");
}

public boolean canMoveDown(final int step) {
Expand All @@ -48,6 +48,6 @@ public Row moveDown(final int step) {
return values()[ordinal() + step];
}

throw new IllegalStateException("움직일 수 없는 위치입니다.");
throw new IllegalStateException("움직일 수 없는 위치입니다. row down");
}
}
70 changes: 70 additions & 0 deletions src/main/java/chess/domain/board/ChessBoard.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package chess.domain.board;

import chess.domain.Color;
import chess.domain.Movement;
import chess.domain.Position;
import chess.domain.piece.limited_moving_chess_piece.BlackPawn;
import chess.domain.piece.ChessPiece;
import chess.domain.piece.limited_moving_chess_piece.King;
import chess.domain.piece.limited_moving_chess_piece.None;
import chess.domain.piece.limited_moving_chess_piece.Pawn;
import chess.domain.piece.limited_moving_chess_piece.WhitePawn;
import java.util.List;
import java.util.Map;

public class ChessBoard {

private final Map<Position, ChessPiece> board;
private final ChessPiece blackKing;
private final ChessPiece whiteKing;

public ChessBoard(ChessBoardInitializer initializer) {
blackKing = new King(Color.BLACK);
whiteKing = new King(Color.WHITE);
board = initializer.initialize(blackKing, whiteKing);
}

public ChessPiece getPieceOfPosition(Position position) {
return board.get(position);
}

public void moveAndCapturePiece(Position origin, Position destination) {
ChessPiece movePiece = getPieceOfPosition(origin);
List<Movement> route = movePiece.findRoute(origin, destination);
boolean isExistHurdleOnRoute = checkHurdleExistOnRouteWithoutDestination(origin, route);
ChessPiece targetPiece = getPieceOfPosition(destination);
movePiece.validateCanMove(route, isExistHurdleOnRoute, targetPiece);

if (movePiece.getClass().equals(BlackPawn.class) || movePiece.getClass().equals(WhitePawn.class)) {
((Pawn) movePiece).isMoved();
}

targetPiece.capture();
board.put(origin, new None());
board.put(destination, movePiece);
}

private boolean checkHurdleExistOnRouteWithoutDestination(Position origin, List<Movement> route) {
List<Movement> routeWithoutDestination = route.subList(0, route.size() - 1);
Position origin2 = origin;
for (Movement movement : routeWithoutDestination) {
if (origin2.canMove(movement)) {
origin2 = origin2.move(movement);
if (!getPieceOfPosition(origin2).isEmpty()) {
return true;
}
}
}
return false;
}

public boolean checkOppositeKingCaptured(Color color) {
if (color == Color.BLACK) {
return whiteKing.isCaptured();
}
if (color == Color.WHITE) {
return blackKing.isCaptured();
}
return false;
}
}
Loading