OS_Homework2 (25-01)

Tae_Tae·2025년 4월 7일

[25-1] Operating System

목록 보기
3/5

4-in-a-row 게임을 통해 IPC와 프로세스를 이해하는 과제

gamatch.c

게임의 메인 프로그램


Header Files / Macro

// 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초)

Global var && func

// 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값을 저장하기 위한 변수

  • PID = 0인 경우 프로세스 생성x
  • PID > 0인 경우 프로세스가 존재함

signal_handler :
SIGINT (Ctrl+C) 혹은
SIGALRM (3초가 지나서 타임아웃 발생) 시 프로그램 강제 종료 (SIGKILL로 프로세스 종료 후 exit(0);)

function declear

// 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]);
  • 사용하는 함수 선언

main()

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;
}
  • 실행 명령어가 명확하지 않으면 usage 출력
  • 유효한 경우 agent_xagent_y를 설정하고
  • 시그널 핸들러 등록 후 게임 실행
void print_usage() {
    printf("Usage: ./gamatch -X <agent-binary> -Y <agent-binary>\n");
}
  • 제대로된 실행 방법을 print해줌

run_game()

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 : 총 이동 횟수 (에이전트가 둔 수)

// Init board

보드 초기화 (모든 칸은 '0'으로 setting함)

// Main game loop

while (moves < MAX_STACK * MAX_HEIGHT && winner == 0)
: 보드가 가득 차거나, 승자가 나오거나, 무승부인 경우 까지 반복문 실행

// Create pipe

  • pipe_to_agent : gamatch에서 에이전트로 데이터 전송
  • pipe_from_agent : 에이전트에서 gamatch로 데이터 수신

각 턴마다 새 파이프 생성 -> 통신 안정성 보장

// Create child process

  • fork()로 자식 프로세스 생성

  • 자식 프로세스 :

    • 불필요한 파이프 끝 닫기 to_agent[1], from_agent[0]
    • dup2로 표준 입출력을 파이프에 연결
    • current_player에 따라 execlagent_x 또는 agent_y 실행
  • 부모 프로세스 :
    자식 PID 저장(child_pid_x 또는 child_pid_y)

// Send current player && current board

  • 에이전트에 현재 플레이어 번호(current_player)와 보드 상태(current_board)를 int로 전송

  • 보드는 board[i][j] - '0'로 char('0', '1', '2')를 int(0, 1, 2)로 변환해 에이전트가 기대하는 형식으로 제공

// Set timeout

  • alarm(TIMEOUT) : 3초 타이머 시작
    에이전트 응답 읽기(read)
  • 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");
}
  • 현재 보드의 상태를 출력해줌

// Check invalid input

  • 에이전트가 반환한 move(돌을 둘 곳)가 'A'~'G' 범위를 벗어나면
    current_player의 상대편이 win하게 됨

// Check full column

  • 에이전트가 선택한 스택이 가득 차 있으면 상대 win

// Place stone

  • 유효한 스택에 플레이어의 돌(player_char)을 가장 낮은 빈칸에 배치

check_winner()

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
}
  • 수평, 수직, 대각선으로 플레이어가 승리 조건을 충족했는지 체크

agent.c

게임을 실행할 프로그램
어디에 수를 둘 지 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;
}

0개의 댓글