-
Notifications
You must be signed in to change notification settings - Fork 79
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
[1단계 - 장기] 헤일러(민서현) 미션 제출합니다. #10
Changes from all commits
c05b6eb
bc91c23
88f09bf
fedaaa3
63c152f
5448de9
b7eaff1
627e724
cd7657f
1978af8
2ab255f
5ea034d
921154d
2d39066
216581f
51e5c82
d3021ac
c72d5d9
c1b5611
af7b771
edbd206
6ce8aff
225fe9d
c39fbe5
5473e7c
13db4fa
d88377a
fea65fd
11b48a6
1f87130
344458c
7c3a111
2cb102e
66bec8f
bbd2d83
71a929f
960a831
cd3247a
8d85ddb
6af5c8a
03af29c
fc12f9a
e044c10
e8bc351
4b7db0c
9744b5e
60df63d
08d5848
05e5f28
aa16beb
c268bd1
6270d81
2af89bd
20412e7
6472401
e9282f9
492ddf1
89ebb95
47a2b80
38ad5bc
9fec375
0d1c4dc
7c66006
7edb1a6
a0dd32c
6d9ddd9
6c59923
10a4769
5ed393d
db21822
679785f
0ff42ef
ad6a041
f04434e
6060d63
1d0f997
ccb2f61
4668962
5e26b6c
1bffd19
6999e71
0b7bc22
3f6ea82
f3838b9
f92a46e
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,3 +1,92 @@ | ||
# java-janggi | ||
|
||
장기 미션 저장소 | ||
## 프로그램 실행 흐름 | ||
|
||
### 게임 시작 | ||
|
||
- 게임 시작 안내 메시지를 출력한다. | ||
|
||
``` | ||
장기 게임에 오신 것을 환영합니다. | ||
``` | ||
|
||
### 상마 순서 입력 | ||
|
||
- 상마 순서 결정을 위한 입력을 받는다. | ||
|
||
``` | ||
한나라 상마 순서를 입력해주세요. (예: 3) | ||
1. 상마상마 | ||
2. 상마마상 | ||
3. 마상상마 | ||
4. 마상마상 | ||
1 | ||
|
||
초나라 상마 순서를 입력해주세요. (예: 3) | ||
1. 상마상마 | ||
2. 상마마상 | ||
3. 마상상마 | ||
4. 마상마상 | ||
2 | ||
``` | ||
|
||
- 예외 | ||
- 올바르지 않은 입력이 들어온 경우 에러 메시지를 출력한다. | ||
|
||
``` | ||
초나라 상마 순서를 입력해주세요. (예: 3) | ||
1. 상마상마 | ||
2. 상마마상 | ||
3. 마상상마 | ||
4. 마상마상 | ||
6 | ||
[ERROR] 올바른 상마 순서 번호를 입력해주세요. (예: 3) | ||
``` | ||
|
||
### 기물 위치 초기화 | ||
|
||
- 입력한 상마 순서에 따라 기물 위치를 초기화한다. | ||
|
||
### 이동 커맨드 입력 | ||
|
||
- move src dst 양식으로 입력해야 한다. | ||
- src에 있는 내 기물을 dst로 이동한다. | ||
|
||
``` | ||
현재 턴 : 초나라 | ||
이동할 기물의 현재 위치와 이동할 위치를 입력해주세요. (예: move 1,1 2,1) | ||
move 1,1 2,1 | ||
``` | ||
|
||
- 예외 | ||
- src가 빈 칸인 경우 예외가 발생한다. | ||
- src에 상대 기물이 있는 경우 예외가 발생한다. | ||
- src에서 dst로 이동할 수 없는 경우 예외가 발생한다. | ||
|
||
### 보드 출력 | ||
|
||
- 현재 장기판 상태를 출력한다. | ||
- 팀은 색깔로 구분한다. | ||
- 초나라 : 초록색 | ||
- 한나라 : 빨간색 | ||
|
||
``` | ||
차 상 마 사 ㅁ 사 상 마 차 | ||
ㅁ ㅁ ㅁ ㅁ 왕 ㅁ ㅁ ㅁ ㅁ | ||
ㅁ 포 ㅁ ㅁ ㅁ ㅁ ㅁ 포 ㅁ | ||
병 ㅁ 병 ㅁ 병 ㅁ 병 ㅁ 병 | ||
ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ | ||
ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ ㅁ | ||
병 ㅁ 병 ㅁ 병 ㅁ 병 ㅁ 병 | ||
ㅁ 포 ㅁ ㅁ ㅁ ㅁ ㅁ 포 ㅁ | ||
ㅁ ㅁ ㅁ ㅁ 왕 ㅁ ㅁ ㅁ ㅁ | ||
차 상 마 사 ㅁ 사 마 상 차 | ||
``` | ||
|
||
### 승패 결과 출력 | ||
|
||
- 이동 중간에 왕이 죽으면 승패 결과를 출력한다. | ||
|
||
``` | ||
초나라의 승리입니다. | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import manager.JanggiGameManager; | ||
|
||
public class Application { | ||
|
||
public static void main(String[] args) { | ||
JanggiGameManager janggiGameManager = new JanggiGameManager(); | ||
janggiGameManager.startGame(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,153 @@ | ||||||||||||
package domain.board; | ||||||||||||
|
||||||||||||
import domain.piece.Piece; | ||||||||||||
import domain.piece.PieceType; | ||||||||||||
import domain.piece.Team; | ||||||||||||
import java.util.List; | ||||||||||||
import java.util.Map; | ||||||||||||
import java.util.Set; | ||||||||||||
import java.util.stream.Collectors; | ||||||||||||
|
||||||||||||
public class Board { | ||||||||||||
|
||||||||||||
private final Map<Point, Piece> pieceByPoint; | ||||||||||||
private final PointNodeMapper pointNodeMapper; | ||||||||||||
|
||||||||||||
public Board(final Map<Point, Piece> pieceByPoint, final PointNodeMapper pointNodeMapper) { | ||||||||||||
this.pieceByPoint = pieceByPoint; | ||||||||||||
this.pointNodeMapper = pointNodeMapper; | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean isEnd() { | ||||||||||||
return !isTwoWangsAlive(); | ||||||||||||
} | ||||||||||||
|
||||||||||||
private boolean isTwoWangsAlive() { | ||||||||||||
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. 잘 생각해보면 isEnd의 반대니까 |
||||||||||||
return findTeamsOfWang().containsAll(List.of(Team.CHO, Team.HAN)); | ||||||||||||
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. 추가적으로 워딩을 생각했을 때, 아래와 같은 로직이 네이밍이랑 조금 더 매칭이 잘되지 않을까요? 🤔
Suggested change
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. 장기의 원래 룰대로라면 각 팀의 왕은 1개씩만 존재해야 하지만, 아래와 같은 예외 상황이 발생할 수 있다고 생각해서 팀의 종류까지 확인하게 했어요!
|
||||||||||||
} | ||||||||||||
|
||||||||||||
public Team findWinTeam() { | ||||||||||||
if (!isEnd()) { | ||||||||||||
throw new IllegalStateException("아직 게임이 끝나지 않았습니다."); | ||||||||||||
} | ||||||||||||
Set<Team> foundTeam = findTeamsOfWang(); | ||||||||||||
if (foundTeam.contains(Team.CHO)) { | ||||||||||||
return Team.CHO; | ||||||||||||
} | ||||||||||||
return Team.HAN; | ||||||||||||
Comment on lines
+34
to
+37
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. Team 객체에게 반대 의 Team을 알려달라고 메시지를 보내볼 수 있지 않을까요? 😃 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. 혹시 힌트 좀 주실 수 있을까요? 🥺 |
||||||||||||
} | ||||||||||||
|
||||||||||||
private Set<Team> findTeamsOfWang() { | ||||||||||||
return pieceByPoint.values().stream() | ||||||||||||
.filter(piece -> piece.type() == PieceType.WANG) | ||||||||||||
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. piece 객체에게 왕인지 여부를 메시지를 보내서 물어보면 어떨까요? 😃 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. 현재 piece 객체에게 메시지를 보내서 물어보는 방법은 아래의 3가지가 떠오릅니다. piece.isWang(); 2번 방법: piece.hasType(PieceType.WANG); 3번 방법: 현행 유지 step2.1에서 기물의 종류에 따라 점수를 부여해야 하기 때문에, 모든 기물들의 구체 클래스 타입을 확인해야 하므로 enum PieceType을 사용하지 않기는 어려울 것 같습니다. 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. 다시 생각해보니 추상 메서드로 |
||||||||||||
.map(Piece::team) | ||||||||||||
.collect(Collectors.toSet()); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean canMove(final Point source, final Point destination) { | ||||||||||||
if (!existsPiece(source)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Piece piece = getPieceByPoint(source); | ||||||||||||
return piece.canMove(source, destination, this); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public void movePiece(final Point source, final Point destination) { | ||||||||||||
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. 테스트 해보았는데 move와 6,2 사이에 공백이 2개 입력되어서 해당 예외가 발생한 것 같아요! |
||||||||||||
Piece sourcePiece = getPieceByPoint(source); | ||||||||||||
if (!sourcePiece.canMove(source, destination, this)) { | ||||||||||||
throw new IllegalArgumentException(source + " -> " + destination + " [ERROR] 이동할 수 없는 경로입니다."); | ||||||||||||
} | ||||||||||||
|
||||||||||||
pieceByPoint.put(destination, sourcePiece); | ||||||||||||
removePiece(source); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean existsPiece(final Point point) { | ||||||||||||
if (!existsPoint(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
return pieceByPoint.containsKey(point); | ||||||||||||
Comment on lines
+66
to
+69
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. 아래와 같이 변경해볼 수 있을 것 같아요. 😃
Suggested change
|
||||||||||||
} | ||||||||||||
|
||||||||||||
private boolean existsPoint(final Point point) { | ||||||||||||
return pointNodeMapper.existsPoint(point); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean existsPo(final Point point) { | ||||||||||||
if (!existsPiece(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Piece piece = getPieceByPoint(point); | ||||||||||||
return piece.type() == PieceType.PO; | ||||||||||||
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. piece 객체에게 PO와 타입이 같은지 메시지를 보내볼 수 있을 것 같아요. |
||||||||||||
} | ||||||||||||
|
||||||||||||
public void removePiece(final Point point) { | ||||||||||||
if (!existsPiece(point)) { | ||||||||||||
return; | ||||||||||||
} | ||||||||||||
Piece piece = getPieceByPoint(point); | ||||||||||||
pieceByPoint.remove(point, piece); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean matchTeam(final Point point, final Team team) { | ||||||||||||
if (!existsPiece(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Piece piece = getPieceByPoint(point); | ||||||||||||
return piece.team() == team; | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean hasPieceType(final Point point, final PieceType pieceType) { | ||||||||||||
if (!existsPiece(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Piece piece = getPieceByPoint(point); | ||||||||||||
return piece.type() == pieceType; | ||||||||||||
} | ||||||||||||
|
||||||||||||
private Piece getPieceByPoint(final Point point) { | ||||||||||||
if (!existsPiece(point)) { | ||||||||||||
throw new IllegalArgumentException(point + ": [ERROR] 해당 좌표에 기물이 존재하지 않습니다."); | ||||||||||||
} | ||||||||||||
return pieceByPoint.get(point); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean existsNextPoint(final Point point, final Direction direction) { | ||||||||||||
if (!existsPoint(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Node node = pointNodeMapper.getNodeByPoint(point); | ||||||||||||
return node.hasNextNode(direction); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public Point getNextPoint(final Point point, final Direction direction) { | ||||||||||||
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.
저는 성능적으로 큰 문제만 없다면 부족한 것 보다는 차라리 방어 로직이 과한게 낫다고 생각해요. 👀 또한 getNextPoint와 existsNextPoint는 공개 메서드이기 때문에 언제 어디서 사용되게 될지 모른다고 생각해요. 🤔 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. 이런 부분이 싫으시다면 getNextPoint를 실패했을 때, 예외를 던지지 않고 |
||||||||||||
validateExistPoint(point); | ||||||||||||
Node node = pointNodeMapper.getNodeByPoint(point); | ||||||||||||
Node nextNode = node.getNextNodeByDirection(direction); | ||||||||||||
return pointNodeMapper.getPointByNode(nextNode); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public boolean canMoveByPath(final Point point, final Path path) { | ||||||||||||
if (!existsPoint(point)) { | ||||||||||||
return false; | ||||||||||||
} | ||||||||||||
Node node = pointNodeMapper.getNodeByPoint(point); | ||||||||||||
return node.canMoveByPath(path); | ||||||||||||
} | ||||||||||||
|
||||||||||||
public Point getPointMovedByPath(final Point point, final Path path) { | ||||||||||||
validateExistPoint(point); | ||||||||||||
Node node = pointNodeMapper.getNodeByPoint(point); | ||||||||||||
return pointNodeMapper.getPointByNode(node.moveByPath(path)); | ||||||||||||
} | ||||||||||||
|
||||||||||||
private void validateExistPoint(final Point point) { | ||||||||||||
if (!existsPoint(point)) { | ||||||||||||
throw new IllegalArgumentException(point.row() + ", " + point.column() + ": 존재하지 않는 좌표입니다."); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
public Map<Point, Piece> getPieceByPoint() { | ||||||||||||
return pieceByPoint; | ||||||||||||
} | ||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package domain.board; | ||
|
||
import domain.piece.Byeong; | ||
import domain.piece.Cha; | ||
import domain.piece.Ma; | ||
import domain.piece.Piece; | ||
import domain.piece.PieceType; | ||
import domain.piece.Po; | ||
import domain.piece.Sa; | ||
import domain.piece.Sang; | ||
import domain.piece.Team; | ||
import domain.piece.Wang; | ||
import java.util.ArrayDeque; | ||
import java.util.Deque; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import view.SangMaOrderCommand; | ||
|
||
public class BoardGenerator { | ||
|
||
public Board generateBoard(final SangMaOrderCommand hanSangMaOrderCommand, | ||
final SangMaOrderCommand choSangMaOrderCommand) { | ||
PointNodeMapperFactory pointNodeMapperFactory = new PointNodeMapperFactory(); | ||
PointNodeMapper pointNodeMapper = pointNodeMapperFactory.createDefaultPointNodeMapper(); | ||
Map<Point, Piece> pieceByPoint = createPieces(hanSangMaOrderCommand, choSangMaOrderCommand); | ||
|
||
return new Board(pieceByPoint, pointNodeMapper); | ||
} | ||
|
||
private Map<Point, Piece> createPieces( | ||
final SangMaOrderCommand hanSangMaOrderCommand, | ||
final SangMaOrderCommand choSangMaOrderCommand) { | ||
Map<Point, Piece> pieceByPoint = new HashMap<>(); | ||
List<Point> hanSangMaPoints = List.of(Point.of(1, 2), Point.of(1, 3), Point.of(1, 7), Point.of(1, 8)); | ||
initializeHanPieces(hanSangMaPoints, hanSangMaOrderCommand, pieceByPoint); | ||
|
||
List<Point> choSangMaPoints = List.of(Point.of(10, 2), Point.of(10, 3), Point.of(10, 7), Point.of(10, 8)); | ||
initializeChoPieces(choSangMaPoints, choSangMaOrderCommand, pieceByPoint); | ||
|
||
return pieceByPoint; | ||
} | ||
|
||
private void initializeHanPieces(final List<Point> sangMaPoints, | ||
final SangMaOrderCommand sangMaOrderCommand, | ||
final Map<Point, Piece> pieceByPoint) { | ||
pieceByPoint.put(Point.of(4, 1), new Byeong(Team.HAN)); | ||
pieceByPoint.put(Point.of(4, 3), new Byeong(Team.HAN)); | ||
pieceByPoint.put(Point.of(4, 5), new Byeong(Team.HAN)); | ||
pieceByPoint.put(Point.of(4, 7), new Byeong(Team.HAN)); | ||
pieceByPoint.put(Point.of(4, 9), new Byeong(Team.HAN)); | ||
|
||
pieceByPoint.put(Point.of(3, 2), new Po(Team.HAN)); | ||
pieceByPoint.put(Point.of(3, 8), new Po(Team.HAN)); | ||
|
||
pieceByPoint.put(Point.of(2, 5), new Wang(Team.HAN)); | ||
|
||
pieceByPoint.put(Point.of(1, 1), new Cha(Team.HAN)); | ||
pieceByPoint.put(Point.of(1, 4), new Sa(Team.HAN)); | ||
pieceByPoint.put(Point.of(1, 6), new Sa(Team.HAN)); | ||
pieceByPoint.put(Point.of(1, 9), new Cha(Team.HAN)); | ||
Deque<Piece> sangMaOrder = createSangMaByCommand(sangMaOrderCommand, Team.HAN); | ||
for (Point point : sangMaPoints) { | ||
pieceByPoint.put(point, sangMaOrder.removeFirst()); | ||
} | ||
} | ||
|
||
private void initializeChoPieces(final List<Point> sangMaPoints, | ||
final SangMaOrderCommand sangMaOrderCommand, | ||
final Map<Point, Piece> pieceByPoint) { | ||
pieceByPoint.put(Point.of(7, 1), new Byeong(Team.CHO)); | ||
pieceByPoint.put(Point.of(7, 3), new Byeong(Team.CHO)); | ||
pieceByPoint.put(Point.of(7, 5), new Byeong(Team.CHO)); | ||
pieceByPoint.put(Point.of(7, 7), new Byeong(Team.CHO)); | ||
pieceByPoint.put(Point.of(7, 9), new Byeong(Team.CHO)); | ||
|
||
pieceByPoint.put(Point.of(8, 2), new Po(Team.CHO)); | ||
pieceByPoint.put(Point.of(8, 8), new Po(Team.CHO)); | ||
|
||
pieceByPoint.put(Point.of(9, 5), new Wang(Team.CHO)); | ||
|
||
pieceByPoint.put(Point.of(10, 1), new Cha(Team.CHO)); | ||
pieceByPoint.put(Point.of(10, 4), new Sa(Team.CHO)); | ||
pieceByPoint.put(Point.of(10, 6), new Sa(Team.CHO)); | ||
pieceByPoint.put(Point.of(10, 9), new Cha(Team.CHO)); | ||
Deque<Piece> sangMaOrder = createSangMaByCommand(sangMaOrderCommand, Team.CHO); | ||
for (Point point : sangMaPoints) { | ||
pieceByPoint.put(point, sangMaOrder.removeFirst()); | ||
} | ||
} | ||
|
||
private Deque<Piece> createSangMaByCommand(final SangMaOrderCommand sangMaOrderCommand, | ||
final Team team) { | ||
List<PieceType> pieceTypes = sangMaOrderCommand.getPieceTypes(); | ||
Deque<Piece> pieces = new ArrayDeque<>(); | ||
for (PieceType pieceType : pieceTypes) { | ||
pieces.addLast(createPiece(pieceType, team)); | ||
} | ||
return pieces; | ||
} | ||
|
||
private Piece createPiece(final PieceType pieceType, final Team team) { | ||
return switch (pieceType) { | ||
case SANG -> new Sang(team); | ||
case MA -> new Ma(team); | ||
default -> throw new IllegalArgumentException("[ERROR] 상 또는 마가 아닙니다."); | ||
}; | ||
} | ||
} |
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.
아무래도 첫 리뷰 요청 마감 기간이 있다보니 시간이 조금 빠듯하셔서 프롤로그까지 작성하시기가 어려우셨을 것 같아요.
다음 리뷰 요청 전에는 프롤로그 작성도 같이 부탁드릴게요. 🙏😉