C++과 SFML 네트워크 라이브러리를 활용하여 제작한 실시간 멀티플레이어 오목 게임입니다.
TCP 소켓 프로그래밍을 통한 네트워크 통신과 Windows Console API를 활용한 콘솔 UI를 구현했습니다.
친구들과 함께 플레이할 수 있는 네트워크 기반 오목 게임을 제작했습니다.
서버-클라이언트 아키텍처를 사용하여 실시간 대전이 가능하며, 게임 종료 후 재대결 기능까지 구현했습니다.
┌─────────────┐ TCP Socket ┌─────────────┐
│ Server │ ←─────────────────────────→ │ Client │
│ (흑돌 선공) │ Port: 53000 │ (백돌 후공) │
│ │ Packet: row, col │ │
│ isMyTurn=T │ │ isMyTurn=F │
└─────────────┘ └─────────────┘
- TCP 소켓 기반 실시간 멀티플레이어: 서버-클라이언트 구조로 네트워크 대전 지원
- 턴제 게임 로직: 비동기 입력 처리와 턴 동기화 구현
- 5목 승리 판정: 4방향 탐색 알고리즘을 통한 승리 조건 검증
- 재대결 시스템: 양측 동의 기반 게임 리셋 및 재시작 프로토콜
- 실시간 콘솔 UI: Windows Console API를 활용한 커서 제어 및 색상 표시
- 언어: C++17
- 라이브러리: SFML 2.5.1 (Network 모듈)
- IDE: Visual Studio 2022
이 프로젝트는 네트워크 동기화·실시간 입출력·알고리즘 최적화를 고려해 설계
프로젝트는 Separation of Concerns 원칙에 따라 3개의 클래스로 구조화
-
GameManager (Controller): 게임 로직, 네트워크 통신, 턴 관리를 총괄
Roleenum으로 서버/클라이언트 역할 구분isMyTurn플래그를 통한 턴 기반 상태 관리- 게임 루프의 핵심 흐름 제어
-
Board (Model): 15×15 게임 보드 상태 관리
vector<vector<char>>자료구조로 2차원 보드 표현- 경계 검사와 중복 배치 검증 로직 캡슐화
- 불변성 보장을 위한 const 메서드 설계
-
Display (View): Windows Console API 기반 렌더링
SetConsoleCursorPosition()으로 특정 좌표에 직접 출력- 색상 변경으로 흑돌/백돌 구분
- 부분 렌더링 최적화로 깜빡임 최소화
SFML Network 모듈을 활용한 블로킹 소켓 방식으로 연결이 확정된 후 게임을 시작
-
서버 모드:
TcpListener로 지정 포트에서 연결 요청 대기- 클라이언트 연결 시
TcpSocket객체 생성 - 연결 성공 시 상대방 IP 주소 출력
-
클라이언트 모드:
- 사용자가 입력한 IP 주소로 서버 접속
- 연결 실패 시 에러 메시지와 함께 종료
SFML의 Packet 클래스를 사용하여 구조화된 데이터 전송
Packet packet;
packet << currentRow << currentCol; // 좌표 직렬화
socket.send(packet); // 전송operator<<를 통한 자동 직렬화 (Endianness 자동 처리)- 정수 2개(행, 열)를 하나의 패킷으로 묶어 전송
- 원자적(atomic) 전송 보장
Packet packet;
socket.receive(packet); // 블로킹 수신
int row, col;
packet >> row >> col; // 역직렬화- 데이터가 도착할 때까지 대기
operator>>로 순서대로 데이터 추출
두 플레이어 간의 턴을 동기화하기 위해 State Machine 패턴을 적용
[내 턴] → 돌 배치 → 좌표 전송 → isMyTurn = false
↓
[상대 턴] ← 좌표 수신 ← 대기 ← isMyTurn = false
↓
승리 판정 → 턴 전환 → isMyTurn = true
-
내 턴 처리:
if (isMyTurn) { handleInput(); // 비블로킹 키 입력 placeStone(); // 돌 배치 + 전송 isMyTurn = false; // 턴 종료 }
-
상대 턴 처리:
else { waitForOpponent(); // 블로킹 수신 isMyTurn = true; // 턴 획득 }
동기화 전략:
- 서버는
isMyTurn = true로 시작 (선공 보장) - 클라이언트는
isMyTurn = false로 시작 (후공 보장) - 돌 배치 후 즉시 턴을 넘겨 데드락 방지
- 그래서 네트워크 지연이 있어도 턴 순서는 절대 뒤바뀌지 않음
오목의 승리 조건(5개 연속)을 효율적으로 검증하기 위해 방향 벡터 기반 탐색. 전체 보드 탐색하지 않고, 마지막에 놓은 돌 위치에서 4방향 × 최대 8칸 = 32회 비교 검사.
게임 종료 후 양측의 재대결 의사를 동기화하기 위한 양방향 핸드셰이크 프로토콜. 양측 동의 확인 후 재시작.
- 전송 → 수신 순서를 양측에서 동일하게 유지해서 양측이 모두 수신 대기하면서 발생 가능한 교착 상태를 방지
- 수신 실패 시엔 재대결 거부로 간주하고 상대방이 연결을 끊었을 때 무한 대기 방지
사용자 입력과 네트워크 통신을 Polling 방식으로 처리.
while (!isBattleEnded) {
if (isMyTurn) {
// 키 입력 확인 (비블로킹)
if (currentRow != prevRow || currentCol != prevCol) {
updateScreen(); // 변경된 경우에만 렌더링
}
handleInput();
} else {
waitForOpponent(); // 블로킹 수신
updateScreen();
}
Sleep(100); // 100ms 폴링 주기
}콘솔 환경에서 GUI 같은 사용자 경험을 제공하기 위해 Windows API를 활용해 실시간 화면 업데이트. 커서 위치가 변경된 경우에만 화면을 업데이트하고, 전체 화면을 다시 그리는 대신 변경된 셀만 업데이트함으로 화면 깜박임을 방지.
-
리포지토리 클론
git clone https://github.com/Sarahhan-one/Omok.git cd Omok -
SFML 설정
- SFML 다운로드 (Visual C++ 버전)
- 프로젝트 속성 설정:
C/C++ → 추가 포함 디렉터리:SFML/include링커 → 추가 라이브러리 디렉터리:SFML/lib링커 → 추가 종속성:sfml-network.lib추가
-
빌드 및 실행
Omok.sln열기- 디버그나 실행
서버:
프로그램 실행 → 'S' 입력 → 포트 53000에서 대기
클라이언트:
프로그램 실행 → 'C' 입력 → 서버 IP 입력 (로컬: 127.0.0.1)
- 방향키 (↑↓←→): 커서 이동
- 스페이스바: 돌 배치
- 재대결: 게임 종료 후 아무 키나 입력
- 서버: 흑돌(B) / 클라이언트: 백돌(W)
- 흑돌 선공
- 가로/세로/대각선 5개 연속 시 승리