-
Notifications
You must be signed in to change notification settings - Fork 466
[1,2,3단계 - 체스] 현구막(최현구) 미션 제출합니다. #207
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
Changes from all commits
f61dace
d818954
cfa7661
36c1564
9a35713
f506355
bfd5b25
e6e3712
99750f4
568f0f8
bf34bd5
0db8f46
365b747
0823c1c
001630d
8e9400f
39f6e90
1a0406a
afb789d
f5cb5a0
134e476
0a9a1e5
69b9c72
7807517
2af11aa
7e57ce7
52ca1e0
fbea316
389baa5
c78a5fc
272a9f0
0ed6725
6368cdc
51cf33a
dc2d6a3
e70940b
9fb8074
136388d
f4f804d
5dc2637
59bf8b8
3ec4c3f
0ebdcd4
2d9096d
e42f641
6666d6e
b6d2f09
15bbfd9
9097e6f
40eaedb
971c7eb
4405c26
052cef9
45bc602
b131296
559c6bc
df01b21
5c2c162
f8e94a7
01de0a5
63fcd3d
976054d
6634acc
207464d
9546136
ef41c8b
bc23631
f88d2b5
2708ea1
c47376f
7ee9e09
12bc658
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,64 @@ | ||
# java-chess | ||
체스 게임 구현을 위한 저장소 | ||
|
||
<br> | ||
|
||
## 리팩토링 구상 목록 | ||
- ~~Board를 출력을 위해 좌표를 정렬해둔 객체로만 사용하고, Piece들의 일급 컬렉션을~~ | ||
- List<Map<Position, Piece>> 로 이루어진 Board의 부분을 컬렉션 객체로 감싸기 (Rank) | ||
|
||
<br> | ||
|
||
## 기능 목록 | ||
|
||
- [x] 체스판을 초기화한다. | ||
- [x] 체스판의 x, y 좌표를 나눈다. | ||
- [x] 좌표를 캐싱한다. | ||
- [x] 좌표를 감싼 체스판을 만든다. | ||
- [x] 체스말을 만든다. | ||
- [x] 폰을 만든다. | ||
- [x] 폰의 이동로직 '수직 한 칸' | ||
- [x] 폰의 초기 이동로직 '수직 두 칸' | ||
- [x] 룩을 만든다. | ||
- [x] 룩의 이동로직 '수직수평 무한' | ||
- [x] 나이트를 만든다. | ||
- [x] 나이트의 이동로직 '수직수평 두 칸 후 대각선 한 칸' | ||
- [x] 비숍을 만든다. | ||
- [x] 비숍의 이동로직 '대각선 무한' | ||
- [x] 퀸을 만든다. | ||
- [x] 퀸의 이동로직 '수직수평 대각선 무한' | ||
- [x] 킹을 만든다. | ||
- [x] 킹의 이동로직 '수직수평 대각선 한 칸' | ||
- [x] 좌표를 채우기 위한 빈말 | ||
- [x] 체스말들을 체스판 좌표에 배치시킨다. | ||
- [x] 체스말을 이동시킨다. | ||
- [x] source 좌표에 체스말이 존재하는지 확인한다. | ||
- 좌표가 체스판에 존재하는지 | ||
- Empty 체스말을 둔 경우, 유효한 체스말인지 확인 | ||
- [x] 체스말이 없는 빈 좌표인 경우... (이동불가) | ||
- [x] 적군 체스말이 존재하는 경우... (이동불가) | ||
- [x] 체스판을 벗어나는 좌표인 경우...(이동불가) | ||
- [x] 아군 체스말이 존재하는 경우... (이동가능 + target 좌표 검사 진행) | ||
- [x] target 좌표에 체스말이 존재하는지 확인한다. | ||
- [x] 아군 체스말이 존재하는 경우... (이동불가) | ||
- [x] 체스판을 벗어나는 좌표인 경우...(이동불가) | ||
- [x] 체스말이 없는 빈 좌표인 경우... (이동가능) | ||
- [x] 적군 체스말이 존재하는 경우... (이동가능 + 적군 체스말을 제거하는 추가동작) | ||
- 1. 내가 잡아먹은 체스 말이 '킹'인지 검사하는 방법 | ||
- 2. 현재 체스판에 '킹'이 2개 존재하는지 검사하는 방법 | ||
- [x] 승패 및 점수를 계산한다 | ||
- [x] 'end' 가 입력되는 경우 게임을 종료한다. | ||
- [x] 킹이 잡히는 경우 게임을 종료한다. | ||
- [x] 체스판에 남아있는 체스말의 점수를 총합한다 | ||
- [x] 폰은 1점으로 계산한다. | ||
- [x] 같은 색의 폰이 세로줄에 있는 경우 해당 폰들의 점수를 0.5로 계산한다. | ||
- [x] 룩은 5점으로 계산한다. | ||
- [x] 나이트는 2.5점으로 계산한다. | ||
- [x] 비숍은 3점으로 계산한다. | ||
- [x] 퀸은 9점으로 계산한다. | ||
- [x] 킹은 0점으로 계산한다. | ||
|
||
<br> | ||
|
||
## 질문 목록 | ||
- ~~InitBoardGenerator의 역할을 InitPieces에 통합하는게 어떤지?~~ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
package chess.controller; | ||
|
||
import chess.domain.board.Board; | ||
import chess.domain.command.Command; | ||
import chess.domain.command.Commands; | ||
import chess.domain.command.Status; | ||
import chess.domain.game.ChessGame; | ||
import chess.utils.DtoAssembler; | ||
import chess.view.InputView; | ||
import chess.view.OutputView; | ||
|
||
public class ChessController { | ||
|
||
public void run() { | ||
ChessGame chessGame = new ChessGame(new Board()); | ||
OutputView.printCommandNotice(); | ||
|
||
while (chessGame.isNotEnd()) { | ||
chessGame = new ChessGame(new Board()); | ||
Commands commands = Commands.initCommands(chessGame); | ||
readyChess(chessGame, commands); | ||
runningChess(chessGame, commands); | ||
finishedChess(chessGame, commands); | ||
} | ||
} | ||
|
||
private void readyChess(ChessGame chessGame, Commands commands) { | ||
OutputView.printRequestInputStart(); | ||
while (chessGame.isInit()) { | ||
executeCommand(commands); | ||
} | ||
} | ||
|
||
private void runningChess(ChessGame chessGame, Commands commands) { | ||
OutputView.printChessStarted(); | ||
while (chessGame.isRunning()) { | ||
OutputView.printChessBoard(DtoAssembler.board(chessGame.ranks())); | ||
executeCommand(commands); | ||
} | ||
} | ||
|
||
private void finishedChess(ChessGame chessGame, Commands commands) { | ||
OutputView.printFinishWithReason(chessGame.winner()); | ||
while (chessGame.isFinished()) { | ||
executeCommand(commands); | ||
} | ||
} | ||
|
||
private void executeCommand(final Commands commands) { | ||
try { | ||
String input = InputView.command(); | ||
Command command = commands.matchedCommand(input); | ||
command.execution(input); | ||
printStatus(command); | ||
} catch (IllegalStateException | IllegalArgumentException e) { | ||
OutputView.printErrorException(e.getMessage()); | ||
} | ||
} | ||
|
||
private void printStatus(Command command) { | ||
if (command.isStatus()) { | ||
Status status = (Status) command; | ||
OutputView.printWinner(status.winner()); | ||
OutputView.printScoreStatus(status.totalWhiteScore(), status.totalBlackScore()); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
package chess.domain.board; | ||
|
||
import chess.domain.board.position.InitPosition; | ||
import chess.domain.board.position.Position; | ||
import chess.domain.movestrategy.MoveStrategy; | ||
import chess.domain.piece.Empty; | ||
import chess.domain.piece.Piece; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class Board { | ||
|
||
public static final int BOTH_KINGS_ALIVE = 2; | ||
public static final String INVALID_POSITION_MESSAGE = "유효하지 않은 좌표 입력입니다."; | ||
|
||
private final List<Rank> ranks; | ||
|
||
public Board() { | ||
this(InitPosition.initRanks()); | ||
} | ||
|
||
public Board(final List<Rank> ranks) { | ||
this.ranks = ranks; | ||
} | ||
|
||
public List<Rank> ranks() { | ||
return this.ranks; | ||
} | ||
|
||
public Piece pieceByPosition(Position position) { | ||
return this.ranks.stream() | ||
.filter(rank -> rank.hasPosition(position)) | ||
.map(map -> map.piece(position)) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException(INVALID_POSITION_MESSAGE)); | ||
} | ||
|
||
public boolean isAliveBothKings() { | ||
return this.ranks.stream() | ||
.flatMap(rank -> rank.pieces().stream()) | ||
.filter(Piece::isKing) | ||
.count() == BOTH_KINGS_ALIVE; | ||
} | ||
|
||
public void moveIfValidPosition(Position source, Position target) { | ||
if (isInvalidPosition(source, target)) { | ||
throw new IllegalArgumentException(INVALID_POSITION_MESSAGE); | ||
} | ||
|
||
swapPieces(source, target); | ||
} | ||
|
||
private boolean isInvalidPosition(Position source, Position target) { | ||
Piece piece = pieceByPosition(source); | ||
MoveStrategy moveStrategy = piece.moveStrategy(); | ||
Set<Position> movablePath = moveStrategy.moveStrategy(this, source); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 더 나은 메서드명은 없을까요? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 바로 위에 있는 메서드에서 |
||
return !movablePath.contains(target); | ||
} | ||
|
||
private void swapPieces(Position source, Position target) { | ||
Piece sourcePiece = pieceByPosition(source); | ||
replacePiece(source, Empty.create()); | ||
replacePiece(target, sourcePiece); | ||
} | ||
|
||
private void replacePiece(Position position, Piece piece) { | ||
Rank foundRank = this.ranks.stream() | ||
.filter(rank -> rank.hasPosition(position)) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException(INVALID_POSITION_MESSAGE)); | ||
|
||
foundRank.replacePiece(position, piece); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package chess.domain.board; | ||
|
||
import chess.domain.board.position.Position; | ||
import chess.domain.piece.Piece; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public class Rank { | ||
|
||
private final Map<Position, Piece> squares; | ||
|
||
public Rank(final Map<Position, Piece> squares) { | ||
this.squares = squares; | ||
} | ||
|
||
public boolean hasPosition(final Position position) { | ||
return this.squares.containsKey(position); | ||
} | ||
|
||
public void replacePiece(final Position position, final Piece piece) { | ||
this.squares.replace(position, piece); | ||
} | ||
|
||
public Piece piece(Position position) { | ||
return this.squares.get(position); | ||
} | ||
|
||
public List<Piece> pieces() { | ||
return new ArrayList<>(this.squares.values()); | ||
} | ||
|
||
public Map<Position, Piece> squares() { | ||
return this.squares; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일급컬렉션에서는 담당한 컬렉션을 외부로 반환해야할 때, 외부에서 조작할 수 있으므로 항상 주의해야 합니다.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일급컬렉션을 일급컬렉션답게 사용하지 못하고 있었네요 😢 |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package chess.domain.board.position; | ||
|
||
import chess.domain.board.Rank; | ||
import chess.domain.piece.Bishop; | ||
import chess.domain.piece.Empty; | ||
import chess.domain.piece.King; | ||
import chess.domain.piece.Knight; | ||
import chess.domain.piece.Pawn; | ||
import chess.domain.piece.Piece; | ||
import chess.domain.piece.Queen; | ||
import chess.domain.piece.Rook; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
public enum InitPosition { | ||
BLACK_ROOK(Xpoint.getRookPoints(), Ypoint.getBlackPoint(), Rook.createBlack()), | ||
WHITE_ROOK(Xpoint.getRookPoints(), Ypoint.getWhitePoint(), Rook.createWhite()), | ||
BLACK_KNIGHT(Xpoint.getKnightPoints(), Ypoint.getBlackPoint(), Knight.createBlack()), | ||
WHITE_KNIGHT(Xpoint.getKnightPoints(), Ypoint.getWhitePoint(), Knight.createWhite()), | ||
BLACK_BISHOP(Xpoint.getBishopPoints(), Ypoint.getBlackPoint(), Bishop.createBlack()), | ||
WHITE_BISHOP(Xpoint.getBishopPoints(), Ypoint.getWhitePoint(), Bishop.createWhite()), | ||
BLACK_QUEEN(Xpoint.getQueenPoint(), Ypoint.getBlackPoint(), Queen.createBlack()), | ||
WHITE_QUEEN(Xpoint.getQueenPoint(), Ypoint.getWhitePoint(), Queen.createWhite()), | ||
BLACK_KING(Xpoint.getKingPoint(), Ypoint.getBlackPoint(), King.createBlack()), | ||
WHITE_KING(Xpoint.getKingPoint(), Ypoint.getWhitePoint(), King.createWhite()), | ||
BLACK_PAWN(Xpoint.getPawnOrEmptyPoints(), Ypoint.getBlackPawnPoint(), Pawn.createBlack()), | ||
WHITE_PAWN(Xpoint.getPawnOrEmptyPoints(), Ypoint.getWhitePawnPoint(), Pawn.createWhite()), | ||
EMPTY_PIECE(Xpoint.getPawnOrEmptyPoints(), Ypoint.getEmptyPoints(), Empty.create()); | ||
|
||
private final List<Xpoint> xPoints; | ||
private final List<Ypoint> yPoints; | ||
private final Piece piece; | ||
|
||
InitPosition(final List<Xpoint> xPoints, final List<Ypoint> yPoints, final Piece piece) { | ||
this.xPoints = xPoints; | ||
this.yPoints = yPoints; | ||
this.piece = piece; | ||
} | ||
|
||
public static List<Rank> initRanks() { | ||
List<Rank> ranks = new ArrayList<>(); | ||
for (Ypoint ypoint : Ypoint.values()) { | ||
ranks.add(initRank(ypoint)); | ||
} | ||
return ranks; | ||
} | ||
|
||
private static Rank initRank(final Ypoint ypoint) { | ||
Map<Position, Piece> line = new LinkedHashMap<>(); | ||
for (Xpoint xpoint : Xpoint.values()) { | ||
line.put(Position.of(xpoint, ypoint), InitPosition.findPiece(xpoint, ypoint)); | ||
} | ||
return new Rank(line); | ||
} | ||
|
||
private static Piece findPiece(final Xpoint xpoint, final Ypoint ypoint) { | ||
return Arrays.stream(values()) | ||
.filter(p -> p.containsXY(xpoint, ypoint)) | ||
.findFirst() | ||
.orElseThrow(() -> new IllegalArgumentException("해당좌표의 체스말을 찾지 못했습니다.")) | ||
.piece; | ||
} | ||
|
||
private boolean containsXY(final Xpoint xpoint, final Ypoint ypoint) { | ||
return this.xPoints.contains(xpoint) && this.yPoints.contains(ypoint); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
실행하다보니 다음과 같은 경우가 있네요.
move a1 a3
에서 a1에 말이 없는 경우) 메시지가null
로 찍힘end
로 종료 시 한번에 종료되지 않음 (두번 입력해야 종료)