Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,24 @@

체스 미션 저장소

## 우아한테크코스 코드리뷰
## 게임 설명

```
RNBQKBNR 8 (rank 8)
PPPPPPPP 7 (rank 7)
........ 6
........ 5
........ 4
........ 3
pppppppp 2
rnbqkbnr 1 (rank 1)

abcdefgh
```

- 체스판에서 각 진영은 검은색(대문자)과 흰색(소문자) 편으로 구분한다.
- 각 진영이 번갈아가면서 기물을 움직여야 하며 흰색(소문자)가 먼저 움직인다.
- 게임 시작: start
- 게임 종료: end
- 게임 이동: move source target - 예) move b2 b3

- [온라인 코드 리뷰 과정](https://github.com/woowacourse/woowacourse-docs/blob/master/maincourse/README.md)
74 changes: 74 additions & 0 deletions src/main/java/chess/Game.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package chess;

import chess.board.Board;
import chess.position.Column;
import chess.position.Position;
import chess.position.Row;
import chess.view.BoardView;
import java.util.Map;
import java.util.Scanner;

public class Game {
private final BoardView boardView = new BoardView();
private final Scanner scanner = new Scanner(System.in);
private final Map<Character, Column> columnInput = Map.of(
'A', Column.A,
'B', Column.B,
'C', Column.C,
'D', Column.D,
'F', Column.F,
'G', Column.G,
'H', Column.H);
private final Map<Character, Row> rowInput = Map.of(
'1', Row.ONE,
'2', Row.TWO,
'3', Row.THREE,
'4', Row.FOUR,
'5', Row.FIVE,
'6', Row.SIX,
'7', Row.SEVEN);

public static void main(String[] args) {
new Game().start();
}

public void start() {

System.out.println("\n"
+ "RNBQKBNR 8 (rank 8)\n"
+ "PPPPPPPP 7 (rank 7)\n"
+ "........ 6\n"
+ "........ 5\n"
+ "........ 4\n"
+ "........ 3\n"
+ "pppppppp 2\n"
+ "rnbqkbnr 1 (rank 1)\n"
+ "abcdefgh\n"
+ "\n"
+ "- 체스판에서 각 진영은 검은색(대문자)과 흰색(소문자) 편으로 구분한다.\n"
+ "- 각 진영이 번갈아가면서 기물을 움직여야 하며 흰색(소문자)가 먼저 움직인다.\n"
+ "- 게임 종료: end\n"
+ "- 게임 이동: move source target - 예) move B2 B3");

Board board = new Board();
String input = "start";
while (true) {
boardView.display(board);
input = scanner.nextLine();
if (input.equals("end")) {
return;
}
char srcColumn = input.charAt(5);
char srcRow = input.charAt(6);
char desColumn = input.charAt(8);
char desRow = input.charAt(9);
try {
Position start = new Position(rowInput.get(srcRow), columnInput.get(srcColumn));
Position end = new Position(rowInput.get(desRow), columnInput.get(desColumn));
board.move(start, end);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
}
158 changes: 158 additions & 0 deletions src/main/java/chess/board/Board.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package chess.board;

import chess.piece.Bishop;
import chess.piece.King;
import chess.piece.Knight;
import chess.piece.Pawn;
import chess.piece.Piece;
import chess.piece.PieceType;
import chess.piece.Queen;
import chess.piece.Rook;
import chess.position.Column;
import chess.position.Movement;
import chess.position.Position;
import chess.position.Row;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

public final class Board {

private final Map<Position, Color> colors;
private final Map<Position, Piece> pieces;
private Color turn = Color.WHITE;

{
// 기본 Empty
Map<Position, Color> colors = new HashMap<>();
for (Row row : Row.values()) {
for (Column column : Column.values()) {
colors.put(new Position(row, column), Color.EMPTY);
}
}
this.colors = colors;
}

public Board(Map<Position, Color> colors, Map<Position, Piece> pieces) {
for (Entry<Position, Color> newColorEntry : colors.entrySet()) {
Position position = newColorEntry.getKey();
Color color = newColorEntry.getValue();
this.colors.put(position, color);
}
this.pieces = new HashMap<>(pieces);
}

public Board() {
// 색깔 넣기
for (Column column : Column.values()) {
// Rank 8, 7 -> Black
colors.put(new Position(Row.EIGHT, column), Color.BLACK);
colors.put(new Position(Row.SEVEN, column), Color.BLACK);
// Rank 2, 1 -> White
colors.put(new Position(Row.TWO, column), Color.WHITE);
colors.put(new Position(Row.ONE, column), Color.WHITE);
}
// 기물 넣기
Map<Position, Piece> pieces = new HashMap<>();
for (Column column : Column.values()) { // 폰
pieces.put(new Position(Row.SEVEN, column), Pawn.black());
pieces.put(new Position(Row.TWO, column), Pawn.white());
}
for (Row row : List.of(Row.ONE, Row.EIGHT)) { // 폰을 제외한 기물
pieces.put(new Position(row, Column.A), Rook.create());
pieces.put(new Position(row, Column.B), Knight.create());
pieces.put(new Position(row, Column.C), Bishop.create());
pieces.put(new Position(row, Column.D), Queen.create());
pieces.put(new Position(row, Column.E), King.create());
pieces.put(new Position(row, Column.F), Bishop.create());
pieces.put(new Position(row, Column.G), Knight.create());
pieces.put(new Position(row, Column.H), Rook.create());
}
this.pieces = pieces;
}

public void move(Position start, Position end) {
Piece piece = findPiece(start);
Color color = colorAt(start);
if (color != turn) {
throw new IllegalArgumentException("[ERROR] 선택한 색깔의 턴이 아닙니다.");
}
validateEndPosition(start, end);
piece.validateMove(start, end);
if (piece.type().canBeBlocked()) {
validateRouteNotBlocked(piece.getValidateMovement(start, end), start, end);
}
if (piece.type() == PieceType.PAWN) {
validatePawn(piece, start, end);
}
pieces.put(end, piece);
colors.put(end, color);
pieces.remove(start);
colors.put(start, Color.EMPTY);
piece.recordMoved();
turn = turn.opposite();
}

private void validatePawn(Piece piece, Position start, Position end) {
Movement validateMovement = piece.getValidateMovement(start, end);
if (validateMovement == Movement.UP_UP) {
if (piece.moved()) {
throw new IllegalArgumentException("[ERROR] 폰은 첫 움직임에만 두 칸 이동할 수 있습니다.");
}
if (colorAt(start.move(Movement.UP)) != Color.EMPTY) {
throw new IllegalArgumentException("[ERROR] 다른 기물에 의해 막혀서 이동할 수 없는 경로입니다.");
}
}
if (validateMovement == Movement.DOWN_DOWN) {
if (piece.moved()) {
throw new IllegalArgumentException("[ERROR] 폰은 첫 움직임에만 두 칸 이동할 수 있습니다.");
}
if (colorAt(start.move(Movement.DOWN)) != Color.EMPTY) {
throw new IllegalArgumentException("[ERROR] 다른 기물에 의해 막혀서 이동할 수 없는 경로입니다.");
}
}
if (validateMovement.isDiagonal() && colorAt(end) == Color.EMPTY) {
throw new IllegalArgumentException("[ERROR] 해당 위치에 잡을 수 있는 기물이 없습니다.");
}
}

private void validateRouteNotBlocked(Movement movement, Position start, Position end) {
Position nextPosition = start;
while (!(nextPosition = nextPosition.move(movement)).equals(end)) { // end 끝까지 가면 끝
if (pieces.containsKey(nextPosition)) { // 이 위치에 어떤 기물이 있는 경우
throw new IllegalArgumentException("[ERROR] 다른 기물에 의해 막혀서 이동할 수 없는 경로입니다.");
}
}
}

private void validateEndPosition(Position start, Position end) {
if (colorAt(end).isEmpty()) {
return;
}
if (colorAt(end) == colorAt(start)) {
throw new IllegalArgumentException("[ERROR] 같은 팀의 기물은 잡을 수 없습니다.");
}
}

public Color colorAt(Position position) {
if (!colors.containsKey(position)) {
throw new IllegalStateException("[ERROR] 해당 위치의 색깔을 찾을 수 없습니다.");
}
return colors.get(position);
}

public PieceType pieceTypeAt(Position position) {
if (!pieces.containsKey(position)) {
throw new IllegalStateException("[ERROR] 해당 위치에 기물이 없습니다.");
}
return pieces.get(position).type();
}

private Piece findPiece(Position position) {
if (!pieces.containsKey(position)) {
throw new IllegalArgumentException("[ERROR] 해당 위치에 기물이 없습니다.");
}
return pieces.get(position);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package chess;
package chess.board;

public enum Color {

Expand Down
33 changes: 32 additions & 1 deletion src/main/java/chess/piece/Bishop.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
package chess.piece;

public class Bishop {
import static chess.position.Movement.LEFT_DOWN;
import static chess.position.Movement.LEFT_UP;
import static chess.position.Movement.RIGHT_DOWN;
import static chess.position.Movement.RIGHT_UP;

import chess.position.Movement;
import chess.position.Position;
import java.util.List;

public final class Bishop extends Piece {

private Bishop(List<Movement> movements, PieceType pieceType) {
super(movements, pieceType);
}

public static Bishop create() {
return new Bishop(List.of(LEFT_UP, LEFT_DOWN, RIGHT_UP, RIGHT_DOWN), PieceType.BISHOP);
}

@Override
public void validateMove(Position start, Position end) {
for (Movement movement : movements) {
Position nextPosition = start;
while (nextPosition.canMove(movement)) { // 보드 끝까지 가면 끝
nextPosition = nextPosition.move(movement); // 1칸 움직임
if (nextPosition.equals(end)) { // 가고자 하는 위치랑 일치함
return;
}
}
}
// 일치하는거 못찾음
throw new IllegalArgumentException("[ERROR] 기물의 이동 규칙에 어긋나는 움직임입니다.");
}
}
33 changes: 32 additions & 1 deletion src/main/java/chess/piece/King.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,36 @@
package chess.piece;

public class King {
import static chess.position.Movement.DOWN;
import static chess.position.Movement.LEFT;
import static chess.position.Movement.RIGHT;
import static chess.position.Movement.UP;

import chess.position.Movement;
import chess.position.Position;
import java.util.List;

public class King extends Piece {

public King(List<Movement> movements, PieceType pieceType) {
super(movements, pieceType);
}

public static King create() {
return new King(List.of(UP, DOWN, LEFT, RIGHT), PieceType.KING);
}

@Override
public void validateMove(Position start, Position end) {
for (Movement movement : movements) {
if (!start.canMove(movement)) {
continue;
}
Position nextPosition = start.move(movement);
if (nextPosition.equals(end)) { // 일치하는게 있음
return;
}
}
// 일치하는거 못찾음
throw new IllegalArgumentException("[ERROR] 기물의 이동 규칙에 어긋나는 움직임입니다.");
}
}
40 changes: 39 additions & 1 deletion src/main/java/chess/piece/Knight.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,43 @@
package chess.piece;

public class Knight {
import static chess.position.Movement.DOWN_DOWN_LEFT;
import static chess.position.Movement.DOWN_DOWN_RIGHT;
import static chess.position.Movement.LEFT_LEFT_DOWN;
import static chess.position.Movement.LEFT_LEFT_UP;
import static chess.position.Movement.RIGHT_RIGHT_DOWN;
import static chess.position.Movement.RIGHT_RIGHT_UP;
import static chess.position.Movement.UP_UP_LEFT;
import static chess.position.Movement.UP_UP_RIGHT;

import chess.position.Movement;
import chess.position.Position;
import java.util.List;

public final class Knight extends Piece {

public Knight(List<Movement> movements, PieceType pieceType) {
super(movements, pieceType);
}

public static Knight create() {
return new Knight(
List.of(LEFT_LEFT_UP, LEFT_LEFT_DOWN, RIGHT_RIGHT_UP, RIGHT_RIGHT_DOWN,
DOWN_DOWN_LEFT, DOWN_DOWN_RIGHT, UP_UP_LEFT, UP_UP_RIGHT),
PieceType.KNIGHT);
}

@Override
public void validateMove(Position start, Position end) {
for (Movement movement : movements) {
if (!start.canMove(movement)) {
continue;
}
Position nextPosition = start.move(movement);
if (nextPosition.equals(end)) { // 일치하는게 있음
return;
}
}
// 일치하는거 못찾음
throw new IllegalArgumentException("[ERROR] 기물의 이동 규칙에 어긋나는 움직임입니다.");
}
}
Loading