-
Notifications
You must be signed in to change notification settings - Fork 13
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
base: seongbeenkim
Are you sure you want to change the base?
Conversation
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.
깔끔하게 잘 구현해 주셨네요 👍
많은 고민을 하시고 잘 녹여주신 것 같아요 ㅎㅎ
몇 가지 코멘트 남겼으니 확인 부탁드려요 🙂
private static final Map<String, Position> POSITIONS = createPositions(); | ||
|
||
private static Map<String, Position> createPositions() { | ||
Map<String, Position> positions = new LinkedHashMap<>(); |
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.
오 HashMap이 아니라 LinkedHashMap을 사용한 이유가 있으실까요? :)
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.
초기 체스판 각 기물들이 위치가 순서대로 나열되어 있는 이미지를 그리면서 구현하여 LinkedHashMap
으로 해주었습니다.
Position
는 캐싱 값을 가지고 반환해주는 역할만 가지고 있기 때문에 HashMap
으로 해도 되네요.
수정해주었습니다!
Arrays.stream(File.values()) | ||
.forEach(file -> Arrays.stream(Rank.values()).forEach(rank -> positions.put(createKey(file, rank), new Position(file, rank)))); |
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.
가독성 측면에서 어떤 생성을 하고 있는지 한 눈에 알아보기가 어렵네요 ㅎㅎ
메서드를 추출하여 가독성을 개선해보면 어떨까요? :)
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.
넵넵 제가 작성한 코드임에도 리딩하는데 시간이 오래 걸리네요..😂
피드백 주신 방법대로 메소드 추출하는 방식으로 변경해주었습니다!
} | ||
|
||
public static MovePattern queenPattern() { | ||
return new MovePattern(DIAGONAL_COORDINATES, CARDINAL_COORDINATES); |
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.
페어나눔 때 이야기하셨지만 퀸은 전 방향 모두 여러 칸 이동 가능합니다 :)
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.
넵넵 퀸은 모두 이동할 수 있게 수정해줬습니다 :)
private final Collection<MoveCoordinate> infiniteMoveCoordinates; | ||
private final Collection<MoveCoordinate> finiteMoveCoordinates; |
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.
음 무한좌표와 유한좌표로 나눠서 관리할 필요가 있을까 하는 의문이 드는데요 ㅎㅎ
source, target이 주어졌을 때, 어떤 말이 움직일 수 있는 단위 방향
만 가지고 있다면
source-target의 단위 방향을 구하고(findMoveCoordinate)
그 말이 단위방향 1칸으로만 움직일 수 있는 말인지, 단위방향 여러 칸으로 움직일 수 있는 말인지에 따라 로직을 전개하면 될 것 같아요 :)
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.
넵넵 피드백주신 것처럼 단위 방향을 통해서 기물을 움직이도록 수정하였습니다 :)
private static final Collection<MoveCoordinate> CARDINAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
NORTH, SOUTH, WEST, EAST | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> DIAGONAL_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
NORTH_EAST, NORTH_WEST, SOUTH_EAST, SOUTH_WEST | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> WHITE_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
WHITE_PAWN_INITIAL_NORTH, NORTH_EAST, NORTH_WEST, NORTH | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> WHITE_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
NORTH_EAST, NORTH_WEST | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> BLACK_PAWN_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
BLACK_PAWN_INITIAL_SOUTH, SOUTH_EAST, SOUTH_WEST, SOUTH | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> BLACK_PAWN_ATTACK_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
SOUTH_EAST, SOUTH_WEST | ||
)); | ||
|
||
private static final Collection<MoveCoordinate> KNIGHT_COORDINATES = Collections.unmodifiableList(Arrays.asList( | ||
NORTH_EAST_LEFT, NORTH_EAST_RIGHT, NORTH_WEST_LEFT, NORTH_WEST_RIGHT, | ||
SOUTH_EAST_LEFT, SOUTH_EAST_RIGHT, SOUTH_WEST_LEFT, SOUTH_WEST_RIGHT | ||
)); |
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.
상수가 엄청 많아졌는데요, Enum을 고려해보면 어떨까요? :)
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.
해당 부분 Enum으로 수정해주니 코드가 더 깔끔해졌네요 감사합니다 :)
private static final String START = "start"; | ||
private static final String END = "end"; | ||
private static final String MOVE = "move"; |
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.
Command도 Enum으로 관리해보면 어떨까요? :)
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.
상수들이기 때문에 Enum으로 관리하는 편이 더 좋겠네요!
Enum 객체로 수정하였습니다 :)
public Set<Position> findPassingPositions(Position target, MoveCoordinate moveCoordinate) { | ||
Set<Position> positions = new HashSet<>(); | ||
Position current = this; | ||
|
||
while (!target.equals(current)) { | ||
current = current.move(moveCoordinate); | ||
positions.add(current); | ||
} | ||
|
||
positions.remove(target); | ||
return positions; | ||
} | ||
|
||
public Collection<Position> findAvailablePositions(final MoveCoordinate moveCoordinate, final boolean isFinite) { | ||
if (isFinite) { | ||
return getFinitePositions(moveCoordinate); | ||
} | ||
return getInfinitePositions(moveCoordinate); | ||
} |
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.
저는 이 기능들이 Position의 책임일까? 하는 생각이 드는데요 ㅎㅎ
메서드 내용을 살펴보면 Position 인스턴스 this를 current에 할당한 뒤
move 하면서 원하는 Collection<Position>
을 추출하고 있는데요,
this를 새로운 current에 할당한다는 점부터 고민해보았을 때,
Position의 인스턴스 메서드로 다루는 것이 과연 맞을까,
이런 책임은 Position이 아니라 Position을 다루는 제 3자 객체가 담당해야 자연스럽지 않을까, 하는 생각이 들어요.
어떻게 보면 Position은 다뤄지는 최소 단위
, 좌표를 나타내기 도구
인 것이고
그 도구를 어떻게 다룰지는 도구가 결정하는 것이 아니라 도구를 사용하는 쪽에서 결정해야 한다고 생각해요 :)
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.
넵넵 피드백 주신 부분에 대해서 동의합니다!
전체 캐싱된 Position을 가지고 있다고 하더라도 내부에서 this를 새로운 current에 할당하고 계속 변경이 일어나기 때문에
자기 자신에 대한 상태만 알고 있어야 하는 하나의 Position 객체가 알 필요 없는 다른 Position 객체에 대한 정보도 알 수 있게 되네요😂
해당 로직을 호출하는 상위 객체인 Piece
에게 위임하는 방식으로 수정하였습니다!
public class AttackPositions { | ||
|
||
private static final int EMPTY = 0; | ||
|
||
private final Map<Position, Integer> counts = new HashMap<>(); | ||
|
||
public AttackPositions(final Map<Position, Piece> pieces) { | ||
pieces.keySet() | ||
.forEach(position -> { | ||
Piece piece = pieces.get(position); | ||
Collection<Position> positions = piece.findAvailableAttackPositions(position); | ||
positions.forEach(this::increase); | ||
}); | ||
} |
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.
저는 이 AttackPositions, Map<Position, Integer>
의 의도나 활용이 잘 이해가 가지 않는데요,
관련해서 설명해주실 수 있으신가요? :)
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.
AttackPositions
은 각 Position(위치)
로 공격할 수 있는 기물이 몇 개인지를 표시한 객체입니다.
예를 들어 게임 초기 시 흰색 플레이어
Position c3
에는 b1 나이트
, b2 폰
이 공격 할 수 있기 때문에 (c3, 2)
Position b3
에는 a2 폰
, c2 폰
이 공격 할 수 있기 때문에 (b3, 2)
이런식으로 저장이 됩니다.
킹
의 스스로 체크 위치로 (잡힐 수 있는 위치) 이동할 수 없습니다
라는 이동 규칙 때문에 만들어주었습니다.
각 플레이어는 자신이 공격할 수 있는 위치에 대한 AttackPositions
를 가지고 있으며 각 기물 이동 시 마다 갱신됩니다.
킹을 선택하여 이동할 시, 상대방의 AttackPositions
을 통해서 자신이 이동할 수 있는 위치를 확인할 수 있게 됩니다.
현재 경로에 상대방 또는 자신의 기물이 존재하여 공격할 수 없는 위치임에도 카운트가 되는 문제가 있어 해결중에 있습니다.... 빠르게 해결하겠습니다..
double pawnScores = calculatePawnScores(pawnPositions); | ||
double sum = pieces.values() | ||
.stream() | ||
.filter(piece -> !piece.isPawn()) |
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.
저는 부정연산자(!
)를 웬만하면 피하는데요 ㅎㅎ
가독성의 문제도 있고, 읽는 사람이 한번 더 해석을 해야하기 때문에 메서드를 제공하는 쪽에서 한번 더 추상화시키면 좋을 것 같아요 ㅎㅎ
piece.isNotPawn()
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.
넵넵! 해당 피드백 읽은 후 부정연산자 사용하는 곳 모두 수정해주었습니다 :)
} | ||
|
||
public void attacked(final Position target) { | ||
if (!hasPieceOn(target)) { |
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.
위에도 부정연산자 피드백을 남겼는데요, 여기도 한번더 추상화시키면 좋을 것 같아요 ㅎㅎ
전체적으로 한번 더 체크해봐도 좋겠네요 :)
안녕하세요 우빈 선생님
체스 게임 1단계 리뷰 요청드립니디!
각 기물이 가진 이동 규칙 특수성(폰, 나이트 등)에 의해 어떻게 역할, 책임을 분리해야할 지 어려움을 많이 겪었던 미션이었습니다ㅠㅠ
이러한 특수성을 가지는 도메인 요구사항이 있을 경우에는 어떻게 공통적인 로직 + 특수성을 모두 챙기면서 객체지향적으로 코드를 작성할 수 있을 지 궁금합니다!