체스말 이동 구현의 어려움
나는 요즘 Blazor를 이용해서 체스를 구현하는 토이 프로젝트를 진행 중이다.
더보기
Blazor: C# 및 HTML을 사용하여 웹 앱을 만들 수 있는 자유-오픈 소스 프레임워크. 서버의 경우 ASP.NET Razor 구문을 사용하여 정의된 UI와 함께 ASP.NET Core 서버 프로세스에서 호스팅된다.
참고) https://ko.wikipedia.org/wiki/%EB%B8%94%EB%A0%88%EC%9D%B4%EC%A0%80_(%EC%9B%B9_%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC)
그런데 생각지도 못한 곳에서 문제가 발생했다. 생각보다 체스말 움직임을 구현하기가 까다로웠던 것이다. 단순히 말을 이동시키는 것뿐만 아니라 다음과 같은 여러 가지 요소를 고려해야 했다.
- 상대 말 체크 – 이동하려는 위치에 상대의 말이 있는지 확인하고, 잡을 수 있는 경우만 이동을 허용해야 한다.
- 내 말 체크 – 같은 편 말 위로 이동하지 못하도록 해야 한다.
- 이동 경로 검사 – 폰이나 나이트를 제외한 대부분의 말(룩, 비숍, 퀸)은 이동 경로에 장애물이 없어야 한다.
- 체크 및 체크메이트 확인 – 현재 위치에서 이동했을 때, 내 왕이 체크 상태가 되는지 검사해야 한다.
이러한 요소를 하나하나 따져가며 구현하려니 생각보다 복잡했다. 특히, 특정 말이 이동했을 때 발생하는 상황을 모두 시뮬레이션해야 하다 보니 로직이 점점 길어지고, 디버깅도 어려워졌다. 나는 어떻게 하면 체스말 이동 로직을 깔끔하고 효율적으로 구현할 수 있을지 고민 중이다.
폰 이동 로직
private void EvaluatePawnMoves()
{
int direction = activeChess.Color == "white" ? -1 : 1;
// 대각선 공격 먼저 확인
int attackRow = activeChess.Row + direction;
// 왼쪽 대각선 공격
if (IsValidPosition(attackRow, activeChess.Column - 1))
{
var oppositeColor = activeChess.Color == "white" ? blackChess : whiteChess;
if (oppositeColor.Any(c => c.Row == attackRow && c.Column == activeChess.Column - 1))
positionPossible.Add(new Position { Row = attackRow, Column = activeChess.Column - 1 });
}
// 오른쪽 대각선 공격
if (IsValidPosition(attackRow, activeChess.Column + 1))
{
var oppositeColor = activeChess.Color == "white" ? blackChess : whiteChess;
if (oppositeColor.Any(c => c.Row == attackRow && c.Column == activeChess.Column + 1))
positionPossible.Add(new Position { Row = attackRow, Column = activeChess.Column + 1 });
}
// 앞으로 한 칸 이동
int newRow = activeChess.Row + direction;
if (IsValidPosition(newRow, activeChess.Column) && !IsOccupied(newRow, activeChess.Column))
{
positionPossible.Add(new Position { Row = newRow, Column = activeChess.Column });
// 처음 이동시 두 칸 이동 가능
if (!activeChess.HasMoved)
{
int twoStepsRow = activeChess.Row + (2 * direction);
if (IsValidPosition(twoStepsRow, activeChess.Column) && !IsOccupied(twoStepsRow, activeChess.Column) && !IsOccupied(newRow, activeChess.Column)) // 중간에 기물이 없어야 함
positionPossible.Add(new Position { Row = twoStepsRow, Column = activeChess.Column });
}
}
}
룩 이동 로직
private void EvaluateRookMoves()
{
// 수직 이동 (위쪽 방향)
for (int row = activeChess.Row - 1; row >= 0; row--)
{
if (!AddMoveAndCheckBlocked(row, activeChess.Column)) break;
}
// 수직 이동 (아래쪽 방향)
for (int row = activeChess.Row + 1; row < 8; row++)
{
if (!AddMoveAndCheckBlocked(row, activeChess.Column)) break;
}
// 수평 이동 (왼쪽 방향)
for (int col = activeChess.Column - 1; col >= 0; col--)
{
if (!AddMoveAndCheckBlocked(activeChess.Row, col)) break;
}
// 수평 이동 (오른쪽 방향)
for (int col = activeChess.Column + 1; col < 8; col++)
{
if (!AddMoveAndCheckBlocked(activeChess.Row, col)) break;
}
}
비숍 이동 로직
private void EvaluateBishopMoves()
{
// 왼쪽 위 대각선
for (int i = 1; i < 8; i++)
{
if (!AddMoveAndCheckBlocked(activeChess.Row - i, activeChess.Column - i)) break;
}
// 오른쪽 위 대각선
for (int i = 1; i < 8; i++)
{
if (!AddMoveAndCheckBlocked(activeChess.Row - i, activeChess.Column + i)) break;
}
// 왼쪽 아래 대각선
for (int i = 1; i < 8; i++)
{
if (!AddMoveAndCheckBlocked(activeChess.Row + i, activeChess.Column - i)) break;
}
// 오른쪽 아래 대각선
for (int i = 1; i < 8; i++)
{
if (!AddMoveAndCheckBlocked(activeChess.Row + i, activeChess.Column + i)) break;
}
}
나이트 이동 로직
private void EvaluateKnightMoves()
{
int[] rowMoves = { -2, -2, -1, -1, 1, 1, 2, 2 };
int[] colMoves = { -1, 1, -2, 2, -2, 2, -1, 1 };
for (int i = 0; i < 8; i++)
{
int newRow = activeChess.Row + rowMoves[i];
int newCol = activeChess.Column + colMoves[i];
AddMoveAndCheckBlocked(newRow, newCol);
}
}
퀸 이동 로직
private void EvaluateQueenMoves()
{
// 퀸: 룩, 비숍의 이동 합
EvaluateRookMoves();
EvaluateBishopMoves();
}
킹 이동 로직
private void EvaluateKingMoves()
{
// 주변 8방향으로 한 칸씩
for (int i = -1; i <= 1; i++)
{
for (int j = -1; j <= 1; j++)
{
if (i == 0 && j == 0) continue;
int newRow = activeChess.Row + i;
int newCol = activeChess.Column + j;
AddMoveAndCheckBlocked(newRow, newCol);
}
}
}
이동 로직을 정리해봤는데 대충 봐도 복잡하다... 조금 더 간단하게 로직을 구현할 수 있을지 생각해보자!

'Side Projects' 카테고리의 다른 글
Node.js에서 OAuth 2.0으로 소셜 로그인 구현하기 (0) | 2025.03.10 |
---|---|
Node.js로 구현하는 AI 챗봇 비교 분석: Gemini vs Grok vs OpenAI (0) | 2025.03.05 |
채팅 시스템 성능 향상시키기 (0) | 2025.02.18 |
Git Actions? (0) | 2025.02.16 |
DI(의존성 주입)? (0) | 2025.01.12 |