4-in-a-row 게임을 통해 IPC와 프로세스를 이해하는 과제
게임의 메인 프로그램
// OS Homework2 Team 208
// Libraries
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
// Define constants
#define COLS 7
#define ROWS 6
#define TIMEOUT 3
COLS : 보드의 열
ROWS : 보드의 행
TIMEOUT : 과제의 조건인 agent의 응답 시간 제한 (3초)
// processes PID variable
pid_t child_pid_x = 0;
pid_t child_pid_y = 0;
// signal handler (SIGINT, SIGALRM)
void signal_handler(int sig) {
if (sig == SIGINT || sig == SIGALRM) {
if (child_pid_x > 0) kill(child_pid_x, SIGKILL);
if (child_pid_y > 0) kill(child_pid_y, SIGKILL);
exit(0);
}
}
pid_t child_pid_x, y :
agentX와 agentY의 PID값을 저장하기 위한 변수
signal_handler :
SIGINT (Ctrl+C) 혹은
SIGALRM (3초가 지나서 타임아웃 발생) 시 프로그램 강제 종료 (SIGKILL로 프로세스 종료 후 exit(0);)
// Function declarations
void print_usage(void);
void run_game(char *agent_x, char *agent_y);
void print_board(char board[ROWS][COLS]);
int check_winner(char board[ROWS][COLS]);
int main(int argc, char *argv[]) {
if (argc != 5 || strcmp(argv[1], "-X") != 0 || strcmp(argv[3], "-Y") != 0) {
print_usage();
return 1;
}
char *agent_x = argv[2];
char *agent_y = argv[4];
signal(SIGINT, signal_handler);
signal(SIGALRM, signal_handler);
run_game(agent_x, agent_y);
return 0;
}
agent_x와 agent_y를 설정하고void print_usage() {
printf("Usage: ./gamatch -X <agent-binary> -Y <agent-binary>\n");
}
void run_game(char *agent_x, char *agent_y) {
char board[ROWS][COLS];
int current_player = 1; // 1 is X, 2 is Y
int winner = 0;
int moves = 0; // Agent's Su
// Init board
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
board[i][j] = '0';
}
}
// Main game loop
while (moves < COLS * ROWS && winner == 0) {
int pipe_to_agent[2], pipe_from_agent[2];
pid_t pid;
char move;
char player_char = (current_player == 1) ? '1' : '2';
int col_index;
char input_buffer[10];
// Create pipe
if (pipe(pipe_to_agent) != 0 || pipe(pipe_from_agent) != 0) {
perror("Pipe Error");
exit(1);
}
// Create child process
pid = fork();
if (pid == -1) {
perror("fork failed");
exit(1);
}
if (pid == 0) {
// Child process
close(pipe_to_agent[1]);
close(pipe_from_agent[0]);
dup2(pipe_to_agent[0], STDIN_FILENO);
dup2(pipe_from_agent[1], STDOUT_FILENO);
close(pipe_to_agent[0]);
close(pipe_from_agent[1]);
if (current_player == 1) {
// AgentX turn
execl(agent_x, agent_x, NULL);
} else {
// AgentY turn
execl(agent_y, agent_y, NULL);
}
perror("execl failed");
exit(1);
}
if (current_player == 1) child_pid_x = pid;
else child_pid_y = pid;
// Parent process
close(pipe_to_agent[0]);
close(pipe_from_agent[1]);
// Send current player
char player_buf[16];
// Convert int to char
player_buf[0] = '0' + current_player;
player_buf[1] = '\n';
int player_len = 2;
if (write(pipe_to_agent[1], player_buf, player_len) == -1) {
perror("write failed");
exit(1);
}
// Send current board
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
char cell_buf[2];
// Convert int to char
cell_buf[0] = (board[i][j] - '0') + '0';
cell_buf[1] = (j < COLS - 1) ? ' ' : '\n';
if (write(pipe_to_agent[1], cell_buf, 2) == -1) {
perror("write failed");
exit(1);
}
}
}
close(pipe_to_agent[1]);
// Set timeout
alarm(TIMEOUT);
read(pipe_from_agent[0], input_buffer, sizeof(input_buffer));
// Clear timeout
alarm(0);
move = input_buffer[0];
close(pipe_from_agent[0]);
printf("\n%c\n", player_char);
print_board(board);
// Check invalid input
if (move < 'A' || move > 'G') {
printf("\nInvalid input! %c wins.\n", (current_player == 1) ? '2' : '1');
winner = (current_player == 1) ? 2 : 1;
break;
}
// Check full column
col_index = move - 'A';
if (board[0][col_index] != '0') {
printf("\nColumn is full! %c wins.\n", (current_player == 1) ? '2' : '1');
winner = (current_player == 1) ? 2 : 1;
break;
}
// Place stone
for (int i = ROWS - 1; i >= 0; i--) {
if (board[i][col_index] == '0') {
board[i][col_index] = player_char;
break;
}
}
moves++;
winner = check_winner(board);
if (winner != 0) break;
current_player = (current_player == 1) ? 2 : 1;
sleep(1);
}
// Print result
if (winner == 0) {
printf("Draw.\n");
} else if (winner == 1) {
printf("Player X wins!\n");
} else {
printf("Player Y wins!\n");
}
// Terminate all processes
if (child_pid_x > 0) kill(child_pid_x, SIGKILL);
if (child_pid_y > 0) kill(child_pid_y, SIGKILL);
wait(NULL);
wait(NULL);
}
board : 빈칸 : '0', '1' : X, '2' : Y 로 게임 보드 저장currnet_player : 현재 플레이어 표시 변수 (1 : X, 2 : Y)winner : 게임 상태 변수 (0: 진행중, 1: X승, 2: Y승, 3: 무승부)moves : 총 이동 횟수 (에이전트가 둔 수)보드 초기화 (모든 칸은 '0'으로 setting함)
while (moves < MAX_STACK * MAX_HEIGHT && winner == 0)
: 보드가 가득 차거나, 승자가 나오거나, 무승부인 경우 까지 반복문 실행
pipe_to_agent : gamatch에서 에이전트로 데이터 전송pipe_from_agent : 에이전트에서 gamatch로 데이터 수신각 턴마다 새 파이프 생성 -> 통신 안정성 보장
fork()로 자식 프로세스 생성
자식 프로세스 :
dup2로 표준 입출력을 파이프에 연결current_player에 따라 execl로 agent_x 또는 agent_y 실행부모 프로세스 :
자식 PID 저장(child_pid_x 또는 child_pid_y)
에이전트에 현재 플레이어 번호(current_player)와 보드 상태(current_board)를 int로 전송
보드는 board[i][j] - '0'로 char('0', '1', '2')를 int(0, 1, 2)로 변환해 에이전트가 기대하는 형식으로 제공
alarm(TIMEOUT) : 3초 타이머 시작read에서 값이 안오면 타이머 터지면서 상대편 승 값이 오면alarm(0) : 타이머 해제.void print_board(char board[ROWS][COLS]) {
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
printf("%c ", board[i][j]);
}
printf("\n");
}
printf("---------------\n");
}
int check_winner(char board[ROWS][COLS]) {
// Check horizontal
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j <= COLS - 4; j++) {
if (board[i][j] != '0' && board[i][j] == board[i][j + 1] &&
board[i][j] == board[i][j + 2] && board[i][j] == board[i][j + 3]) {
return (board[i][j] == '1') ? 1 : 2;
}
}
}
// Check vertical
for (int i = 0; i <= ROWS - 4; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] != '0' && board[i][j] == board[i + 1][j] &&
board[i][j] == board[i + 2][j] && board[i][j] == board[i + 3][j]) {
return (board[i][j] == '1') ? 1 : 2;
}
}
}
// Check diagonal (down right \ )
for (int i = 0; i <= ROWS - 4; i++) {
for (int j = 0; j <= COLS - 4; j++) {
if (board[i][j] != '0' && board[i][j] == board[i + 1][j + 1] &&
board[i][j] == board[i + 2][j + 2] && board[i][j] == board[i + 3][j + 3]) {
return (board[i][j] == '1') ? 1 : 2;
}
}
}
// Check diagonal (down left / )
for (int i = 0; i <= ROWS - 4; i++) {
for (int j = COLS - 1; j >= 3; j--) {
if (board[i][j] != '0' && board[i][j] == board[i + 1][j - 1] &&
board[i][j] == board[i + 2][j - 2] && board[i][j] == board[i + 3][j - 3]) {
return (board[i][j] == '1') ? 1 : 2;
}
}
}
// Check draw
for (int i = 0; i < ROWS; i++) {
for (int j = 0; j < COLS; j++) {
if (board[i][j] == '0') return 0;
}
}
return 3; // Draw
}
게임을 실행할 프로그램
어디에 수를 둘 지 printf해줌
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N_STACKS 7
#define STACK_CAP 6
static int this_player;
static int board[STACK_CAP + 1][N_STACKS + 1];
static int top[N_STACKS + 1] = {0, 1, 1, 1, 1, 1, 1, 1};
int const UPWARD = 1;
int const DOWNWARD = -1;
int const LEFTWARD = -1;
int const RIGHTWARD = 1;
int count_adjacent_stones(int stack, int level, int hdir, int vdir, int player) {
int count = 0;
while (1 <= stack && stack <= N_STACKS && 1 <= level && level <= STACK_CAP) {
if (board[level][stack] == player) {
count++;
stack += hdir;
level += vdir;
} else {
break;
}
}
return count;
}
int count_down(int stack, int level, int player) {
return count_adjacent_stones(stack, level + DOWNWARD, 0, DOWNWARD, player);
}
int count_left(int stack, int level, int player) {
return count_adjacent_stones(stack + LEFTWARD, level, LEFTWARD, 0, player);
}
int count_right(int stack, int level, int player) {
return count_adjacent_stones(stack + RIGHTWARD, level, RIGHTWARD, 0, player);
}
int count_down_left(int stack, int level, int player) {
return count_adjacent_stones(stack + LEFTWARD, level + DOWNWARD, LEFTWARD, DOWNWARD, player);
}
int count_down_right(int stack, int level, int player) {
return count_adjacent_stones(stack + RIGHTWARD, level + DOWNWARD, RIGHTWARD, DOWNWARD, player);
}
int count_up_left(int stack, int level, int player) {
return count_adjacent_stones(stack + LEFTWARD, level + UPWARD, LEFTWARD, UPWARD, player);
}
int count_up_right(int stack, int level, int player) {
return count_adjacent_stones(stack + RIGHTWARD, level + UPWARD, RIGHTWARD, UPWARD, player);
}
int find_winning_move(int player) {
for (int stack = 1; stack <= N_STACKS; stack++) {
if (top[stack] > STACK_CAP) continue;
if (count_down(stack, top[stack], player) == 3) return stack;
if (count_left(stack, top[stack], player) + count_right(stack, top[stack], player) == 3) return stack;
if (count_down_left(stack, top[stack], player) + count_up_right(stack, top[stack], player) == 3) return stack;
if (count_up_left(stack, top[stack], player) + count_down_right(stack, top[stack], player) == 3) return stack;
}
return 0;
}
int find_blocking_move(int player) {
int other_player = 3 - player;
for (int stack = 1; stack <= N_STACKS; stack++) {
if (top[stack] > STACK_CAP) continue;
if (count_down(stack, top[stack], other_player) == 3) return stack;
if (count_left(stack, top[stack], other_player) + count_right(stack, top[stack], other_player) == 3) return stack;
if (count_down_left(stack, top[stack], other_player) + count_up_right(stack, top[stack], other_player) == 3) return stack;
if (count_up_left(stack, top[stack], other_player) + count_down_right(stack, top[stack], other_player) == 3) return stack;
}
return 0;
}
int main() {
scanf("%d", &this_player);
if (this_player != 1 && this_player != 2) return EXIT_FAILURE;
for (int i = STACK_CAP; i > 0; i--) {
for (int j = 1; j <= N_STACKS; j++) {
scanf("%d", &board[i][j]);
if (board[i][j] != 0 && top[j] == 1) top[j] = i + 1;
}
}
int choice = find_winning_move(this_player);
if (choice) {
printf("%c", 'A' + choice - 1);
return EXIT_SUCCESS;
}
choice = find_blocking_move(this_player);
if (choice) {
printf("%c", 'A' + choice - 1);
return EXIT_SUCCESS;
}
srand(time(NULL));
do {
choice = rand() % N_STACKS + 1;
} while (top[choice] > STACK_CAP);
printf("%c", 'A' + choice - 1);
return EXIT_SUCCESS;
}