Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Step1 : 체스 게임 1단계 리뷰 요청 드립니다! #11

Open
wants to merge 42 commits into
base: seongbeenkim
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
3a12a76
feat: 피스 생성 테스트 및 구현
seongbeenkim Aug 16, 2021
667d30a
feat: 피스 위치 캐싱 테스트 및 구현
seongbeenkim Aug 16, 2021
05e7c1d
feat: 체스판 32개의 피스 초기화, 생성 테스트 및 구현
seongbeenkim Aug 16, 2021
2d33803
feat: 기물 종류에 따라 이름을 매핑하는 기물 이름 매퍼 테스트 및 구현
seongbeenkim Aug 17, 2021
cc54492
feat: 체스판 전체 출력하는 기능 구현
seongbeenkim Aug 17, 2021
8bc0bd5
feat: 체스 게임 시작&종료 명령어 입력 테스트 및 구현
seongbeenkim Aug 17, 2021
f6c2eb4
feat: 기물 이동 시 공통 예외 케이스 테스트 및 구현
seongbeenkim Aug 17, 2021
22d29d1
feat: 비숍 이동 규칙 테스트 및 구현
seongbeenkim Aug 17, 2021
c877ccd
feat: 룩 이동 규칙 테스트 및 구현
seongbeenkim Aug 17, 2021
adc18b6
feat: 퀸 이동 규칙 테스트 및 구현
seongbeenkim Aug 17, 2021
f3c877a
feat: 킹 이동 규칙 테스트 및 구현
seongbeenkim Aug 17, 2021
12da4ca
feat: 나이트 이동 규칙 테스트 및 구현
seongbeenkim Aug 17, 2021
44ecf39
feat: 폰 이동 규칙 테스트 및 구현
seongbeenkim Aug 19, 2021
4498862
feat: 체스판에서 피스 이동 테스트 및 구현
seongbeenkim Aug 19, 2021
a40508d
refactor: 피스 색상별 체스판 분리
seongbeenkim Aug 19, 2021
c30f242
feat: 비숍, 룩 공격 가능한 경로 반환하는 테스트 및 기능 구현
seongbeenkim Aug 19, 2021
483c149
feat: 폰 제외한 기물 패턴 테스트 및 기능 구현
seongbeenkim Aug 20, 2021
c2f0dea
feat: 기물 이동 시 공격 가능 위치를 갱신하는 테스트 및 기능 구현
seongbeenkim Aug 20, 2021
c17c852
feat: 킹 이동제한조건 테스트 및 기능 구현, 예외 처리 및 게임 흐름 제어
jiwoo-kimm Aug 20, 2021
5d69a67
feat: 점수 계산 기능 테스트 및 구현
seongbeenkim Aug 20, 2021
be1d67e
feat: 점수 및 게임 결과 출력 기능 구현
jiwoo-kimm Aug 20, 2021
aba4a3a
refactor: 패키지 구조 및 테스트 케이스 정리
jiwoo-kimm Aug 20, 2021
1f8194b
refactor: LinkedHashMap -> HashMap 변경
seongbeenkim Aug 31, 2021
de1c9c7
refactor: Position 캐시 생성 코드 메소드 추출하여 가독성 개선
seongbeenkim Aug 31, 2021
09575d2
refactor: 부정연산자 제거 및 변수 이름 변경
seongbeenkim Aug 31, 2021
b1e17a0
refactor: 가독성을 위한 메소드 추출 및 '==' 제거
seongbeenkim Aug 31, 2021
527e5d9
refactor: ENUM 이름 규칙(대문자 적용) 및 테스트 코드 추가
seongbeenkim Sep 1, 2021
770f4cb
refactor: 테스트 코드 추가
seongbeenkim Sep 1, 2021
2df66a6
refactor: ENUM 클래스 map 자료구조 적용
seongbeenkim Sep 2, 2021
4ef547b
refactor: 변수 이름 변경 및 추상화 수준 한 단계 제거
seongbeenkim Sep 2, 2021
6f89fbf
refactor: MoveCoordinate -> Direction으로 변경
seongbeenkim Sep 2, 2021
f90cb51
refactor: MovePattern ENUM 클래스로 변경
seongbeenkim Sep 2, 2021
93b8d34
refactor: 모든 기물 단위 방향만으로 이동할 수 있게 수정
seongbeenkim Sep 2, 2021
8b1d2f5
refactor: AttackPositions -> AttackRange로 변경
seongbeenkim Sep 2, 2021
123d3d3
refactor: 변수, 함수 이름 변경
seongbeenkim Sep 2, 2021
296dd67
refactor: 이동 경로 구하는 로직 Position -> Piece로 위임
seongbeenkim Sep 2, 2021
3347861
refactor: Set -> Collection 타입 반환 및 메소드 이름 변경
seongbeenkim Sep 2, 2021
361883e
refactor: MoveParameters -> MoveOptions 변경
seongbeenkim Sep 3, 2021
d6edf85
refactor: 명령어 enum으로 분리 및 옵션 포함된 명령어 객체 추가
seongbeenkim Sep 3, 2021
3ef6adb
refactor: 명령어 enum으로 분리 및 옵션 포함된 명령어 객체 추가
seongbeenkim Sep 3, 2021
b21c36a
refactor: CommandOptions에서 MoveOptions 반환하게 수정
seongbeenkim Sep 13, 2021
af13054
refactor: 폰 공격 Direction 제거
seongbeenkim Sep 13, 2021
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
100 changes: 100 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1 +1,101 @@
# java-chess 게임

## 구현해야할 목록

- [x] 입력
- [x] 명령어 입력
- [] 출력
- [x] 시작 안내 문구 출력
- [x] 체스판 전체 출력
- [ ] 점수와 결과 출력

- [x] 체스게임(ChessGame)
- [x] 체스판
- [x] 현재 플레이어
- [x] 게임 시작&종료 상태 제어

- 기능
- [x] 명령어 검증 및 처리
- [x] start : 게임 실행
- [x] end : 게임 종료
- [x] move : 인자로 전달받은 source 위치에서 target 위치로 기물 이동

- [x] 체스판(Board)
- [x] 체스판
- [x] 생성 시 32개의 기물 초기화

- 기능
- [x] 인자로 전달받은 위치에 기물이 있는지 확인
- [x] ERROR : 존재하지 않을 경우
- [x] 같은 색상 기물인지 확인
- [x] ERROR : 다른 색상일 경우
- [x] 시작과 도착 위치의 기물이 다른 색상인지 확인
- [x] ERROR : 같은 색상일 경우
- [x] source 위치에서 target 위치로 기물 이동
- [x] ERROR : source 위치에 기물이 없는 경우
- [x] ERROR : 자신의 기물이 아닌 경우
- [x] ERROR : source, target 위치의 기물 색상이 같을 경우
- [x] ERROR : source, target 위치가 같을 경우
- [x] ERROR : 이동 경로에 기물이 존재할 경우

- [x] 기물(Piece)
- [x] 색상

- [x] 기물 위치(Position)
- [x] 파일
- [x] 랭크
- [x] 64개의 캐싱된 위치

- 기능
- [x] 전달받은 인자에 해당하는 위치 객체 반환

- [x] 기물 색상(Color)
- [x] 색상

- [x] 기물 이름 매퍼
- [x] 기물 종류에 따라 이름을 매핑

- [x] 플레이어(Player)
- [x] 자신의 기물
- [x] 자신이 공격 가능한 범위

- 기능
- [x] 기물을 이동시킨다.
- [x] 기물의 이동 경로를 반환한다.
- [x] 입력받은 위치에 기물이 있는지 확인한다.

## 이동 규칙

- [x] 폰 (1 or 0.5)
- [x] 적 방향 직선 1칸 이동
- [x] ERROR : 직선 방향 2칸 이상일 경우
- [x] ERROR : 좌, 우 이동이 포함될 경우
- [x] 처음 이동 시에는 2칸 이동 가능
- [x] ERROR : 직선 방향 3칸 이상일 경우
- [x] ERROR : 좌, 우 이동이 포함될 경우

- [x] 공격 : 적 방향 좌, 우 대각선 1칸
- [x] ERROR : 직선 1칸 && 좌 또는 우 1칸이 아닐 경우

- [x] 룩 (5)
- [x] 모든 직선 방향으로 원하는 만큼 이동 가능
- [x] ERROR : 룩 이동 패턴으로 이동할 수 없는 위치일 경우

- [x] 나이트 (2.5)
- [x] 모든 직선 방향 1칸 + 이동한 직선 방향의 좌, 우 대각선 1칸으로 이동 가능
- [x] ERROR : 나이트 이동 패턴으로 이동할 수 없는 위치일 경우
- [x] 진행 방향이 가로막혀도 적, 아군 상관없이 뛰어넘을 수 있다.

- [x] 비숍 (3)
- [x] 모든 대각선 방향으로 원하는 만큼 이동 가능
- [x] ERROR : 비숍 이동 패턴으로 이동할 수 없는 위치일 경우

- [x] 퀸 (9)
- [x] 모든 방향 1칸 + α 이동 (모든 대각선 방향으로는 원하는 만큼 이동 가능)
- [x] ERROR : 퀸 이동 패턴으로 이동할 수 없는 위치일 경우

- [x] 킹
- [x] 모든 방향 1칸 이동
- [x] ERROR : 킹 이동 패턴으로 이동할 수 없는 위치일 경우
- [x] 상대의 공격 범위로는 이동 불가능

1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.1'
testCompile "org.assertj:assertj-core:3.14.0"
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.4.2'
}
17 changes: 17 additions & 0 deletions src/main/java/chess/ConsoleApplication.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package chess;

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

public class ConsoleApplication {

public static void main(String[] args) {
OutputView outputView = new ConsoleOutputView();
InputView inputView = new ConsoleInputView();
ChessController chessController = new ChessController(inputView, outputView);
chessController.run();
}
}
46 changes: 46 additions & 0 deletions src/main/java/chess/controller/ChessController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package chess.controller;

import chess.controller.dto.BoardDto;
import chess.domain.ChessGame;
import chess.domain.command.Command;
import chess.view.InputView;
import chess.view.OutputView;

public class ChessController {

private final InputView inputView;
private final OutputView outputView;

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

public void run() {
outputView.printGuide();
ChessGame chessGame = new ChessGame();

while (chessGame.isRunning()) {
try {
Command command = new Command(inputView.getCommand());
runCommand(chessGame, command);
printBoard(chessGame);
} catch (UnsupportedOperationException e) {
System.out.println(e.getMessage());
}
}
}

private void runCommand(ChessGame chessGame, Command command) {
try {
chessGame.run(command);
} catch (IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}

private void printBoard(ChessGame chessGame) {
BoardDto boardDto = new BoardDto(chessGame.getBoard());
outputView.printBoard(boardDto);
}
}
41 changes: 41 additions & 0 deletions src/main/java/chess/controller/dto/BoardDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package chess.controller.dto;

import chess.domain.board.Board;
import chess.domain.board.File;
import chess.domain.board.Position;
import chess.domain.board.Rank;
import chess.domain.piece.Piece;
import chess.domain.piece.mapper.PieceMappers;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class BoardDto {
private List<PositionDto> positionDtos = new ArrayList<>();

public BoardDto(final Board board) {
Arrays.stream(Rank.values()).forEach(rank -> Arrays.stream(
File.values()).forEach(file -> addPositionDto(file, rank, board)));
}

private void addPositionDto(final File file, final Rank rank, final Board board) {
Position position = Position.from(file, rank);

if (board.isEmpty(position)) {
PositionDto positionDto = new PositionDto(position.getFile());
positionDtos.add(positionDto);
return;
}

Piece piece = board.findBy(position);
String name = PieceMappers.findNameBy(piece);
PositionDto positionDto = new PositionDto(position.getFile(), name);
positionDtos.add(positionDto);
}

public List<PositionDto> getPositionDtos() {
return Collections.unmodifiableList(positionDtos);
}
}
28 changes: 28 additions & 0 deletions src/main/java/chess/controller/dto/PositionDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package chess.controller.dto;

import chess.domain.board.File;

public class PositionDto {

private static final String DEFAULT_NAME = ".";

private final boolean isLastFile;
private final String name;

public PositionDto(final File file, final String name) {
this.isLastFile = (file == File.h);
this.name = name;
}

public PositionDto(final File file) {
this(file, DEFAULT_NAME);
}

public boolean isLastFile() {
return isLastFile;
}

public String getName() {
return name;
}
}
46 changes: 46 additions & 0 deletions src/main/java/chess/domain/ChessGame.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package chess.domain;

import chess.domain.board.Board;
import chess.domain.command.Command;
import chess.domain.command.MoveParameters;

public class ChessGame {

private final Board board = new Board();
private boolean isRunning = true;
private boolean isWhiteTurn = true;

public ChessGame() {
}

public void run(final Command command) {
if (command.isStart()) {
return;
}

if (command.isEnd()) {
isRunning = false;
return;
}

if (command.isMove()) {
move(command.getMoveParameters());
return;
}

throw new UnsupportedOperationException("유효하지 않은 명령어입니다.");
}

private void move(final MoveParameters moveParameters) {
board.move(moveParameters, isWhiteTurn);
isWhiteTurn = !isWhiteTurn;
}

public boolean isRunning() {
return isRunning;
}

public Board getBoard() {
return board;
}
}
101 changes: 101 additions & 0 deletions src/main/java/chess/domain/board/Board.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package chess.domain.board;

import chess.domain.command.MoveParameters;
import chess.domain.piece.Color;
import chess.domain.piece.Piece;
import chess.domain.player.Player;

import java.util.Set;

public class Board {
private final Player white;
private final Player black;

public Board() {
this.white = new Player(Color.WHITE);
this.black = new Player(Color.BLACK);
}

public void move(final MoveParameters moveParameters, final boolean isWhiteTurn) {
Player player = currentPlayer(isWhiteTurn);
Player enemy = currentPlayer(!isWhiteTurn);
Position source = moveParameters.getSource();
Position target = moveParameters.getTarget();

validateSourceOwner(enemy, source);
validateSamePosition(source, target);
validateTarget(player, target);
validateKingMovable(player, enemy, source, target);

enemy.attacked(target);
movePiece(player, source, target);
}

private Player currentPlayer(final boolean isWhiteTurn) {
if (isWhiteTurn) {
return white;
}
return black;
}

private void validateSourceOwner(final Player enemy, final Position source) {
if (enemy.hasPieceOn(source)) {
throw new IllegalArgumentException("자신의 기물만 움직일 수 있습니다.");
}
}

private void validateSamePosition(final Position source, final Position target) {
if (source.equals(target)) {
throw new IllegalArgumentException("출발 위치와 도착 위치가 같을 수 없습니다.");
}
}

private void validateTarget(final Player player, final Position target) {
if (player.hasPieceOn(target)) {
throw new IllegalArgumentException("같은 색상의 기물은 공격할 수 없습니다.");
}
}

private void validateKingMovable(final Player player, final Player enemy, final Position source, final Position target) {
if (player.hasKingOn(source) && enemy.canAttack(target)) {
throw new IllegalArgumentException("킹은 상대방이 공격 가능한 위치로 이동할 수 없습니다.");
}
}

private void movePiece(final Player player, final Position source, final Position target) {
Set<Position> paths = player.findPaths(source, target);
validatePathsEmpty(paths);

player.update(source, target);
}

private void validatePathsEmpty(final Set<Position> paths) {
boolean isWhiteBlocked = paths.stream()
.anyMatch(white::hasPieceOn);
boolean isBlackBlocked = paths.stream()
.anyMatch(black::hasPieceOn);

if (isWhiteBlocked || isBlackBlocked) {
throw new IllegalArgumentException("기물을 통과하여 이동할 수 없습니다.");
}
}

public Piece findBy(final Position position) {
if (white.hasPieceOn(position)) {
return white.findPieceBy(position);
}

return black.findPieceBy(position);
}

public boolean isEmpty(final Position position) {
return !white.hasPieceOn(position) && !black.hasPieceOn(position);
}

public Status getStatus() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

점수 계산에 대한 명령어(status) 적용이 안되어 있는 것 같아요! :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗.. 페어분 repo에서 pull하고 푸시를 안했었네요 죄송합니다ㅠㅠ
반영하였습니다!

double whiteScore = white.sumScores();
double blackScore = black.sumScores();

return new Status(whiteScore, blackScore);
}
}
Loading