체스말 이동하기 로직

체스말 이동 구현의 어려움

나는 요즘 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)

그런데 생각지도 못한 곳에서 문제가 발생했다. 생각보다 체스말 움직임을 구현하기가 까다로웠던 것이다. 단순히 말을 이동시키는 것뿐만 아니라 다음과 같은 여러 가지 요소를 고려해야 했다.

  1. 상대 말 체크 – 이동하려는 위치에 상대의 말이 있는지 확인하고, 잡을 수 있는 경우만 이동을 허용해야 한다.
  2. 내 말 체크 – 같은 편 말 위로 이동하지 못하도록 해야 한다.
  3. 이동 경로 검사 – 폰이나 나이트를 제외한 대부분의 말(룩, 비숍, 퀸)은 이동 경로에 장애물이 없어야 한다.
  4. 체크 및 체크메이트 확인 – 현재 위치에서 이동했을 때, 내 왕이 체크 상태가 되는지 검사해야 한다.

이러한 요소를 하나하나 따져가며 구현하려니 생각보다 복잡했다. 특히, 특정 말이 이동했을 때 발생하는 상황을 모두 시뮬레이션해야 하다 보니 로직이 점점 길어지고, 디버깅도 어려워졌다. 나는 어떻게 하면 체스말 이동 로직을 깔끔하고 효율적으로 구현할 수 있을지 고민 중이다.

 

폰 이동 로직

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);
        }
    }
}

 

이동 로직을 정리해봤는데 대충 봐도 복잡하다... 조금 더 간단하게 로직을 구현할 수 있을지 생각해보자!