20231106_기록_틱택토

2023. 11. 6. 21:36IT/TIL

오늘 한 것들

 

프로그래밍 기초 발제 OT 참가

C# 문법 종합반 1주차 수강

C# 문법 종합반 2주차 수강

 

오늘은 C# 문법 종합반 강의를 중점적으로 공부했는데,

이전에 공부했었던 C# 사전 문법 기초로는 부족했었던 부분을 보충해주는 느낌으로 공부했으며,

구글링을 해도 의문점이 남았던 부분들을 보완하는 느낌으로 공부했다.

 

특히, python을 공부하면서 익혔던 연산자들을 활용하는 것은 쉬웠지만,

python에는 없었던 변수 선언 부분이라거나, 조건문과 반복문의 서식이라거나,

배열들을 작업하는 과정에서의 다른 부분은 잘 이해가 되지 않는 부분이 있었는데,

이 기회를 통해 한 호흡으로 정리할 수 있었다.

 

 

오늘의 메인 주제는 틱택토인데

2주차 과제에 있던 녀석으로, 갑작스럽게 과제로 나타났다.

이전에 python을 공부하면서 한 번 구현했던 것으로

생각난 김에 그 때의 파일을 찾아보니 완성본은 사라지고 아래의 중간 작업물만 남아있었다.

오랫만에 python 코드를 보니 추억이 새삼 느껴지고 C#과의 차이점도 확인할 수 있었다.

import pygame
import sys
import random
from pygame.locals import *

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
INNER = (218, 165, 32)

size = [400, 400]
screen = pygame.display.set_mode(size)

x_values = [1, 2, 3]
y_values = [1, 2, 3]


# 마우스 조작 관련

cursor_x = 0
cursor_y = 0
mouse_x = 0
mouse_y = 0
mouse_c = 0

def mouse_move(e):
    global mouse_x, mouse_y
    mouse_x = e.x
    mouse_y = e.y

def mouse_press(e):
    global mouse_c
    mouse_c = 1


# 테이블 만들기

TABLE_X = 3
TABLE_Y = 3
table = []
for y in range(TABLE_Y):
    table.append([0] * TABLE_X)

def draw_table():
    TABLE_X = 3
    TABLE_Y = 3
    table = []
    for y in range(TABLE_Y):
        table.append([0] * TABLE_X)


# 좌표값
"""
p01 = (050, 050), p02 = (150, 050), p03 = (250, 050), p04 = (350, 050)
p05 = (050, 150), p06 = (150, 150), p07 = (250, 150), p08 = (350, 150)
p09 = (050, 250), p10 = (150, 250), p11 = (250, 250), p12 = (350, 250)
p13 = (050, 350), p14 = (150, 350), p15 = (250, 350), p16 = (350, 350)
s01 = (100, 100), s02 = (200, 100), s03 = (300, 100)
s04 = (100, 200), s05 = (200, 200), s06 = (300, 200)
s07 = (100, 300), s08 = (200, 300), s09 = (300, 300)
"""
def point():
    global x_values, y_values
    points = []
    for i in range(3):
        point.append([0])
    return points


# 게임판 그리기
def draw_board():
    pygame.draw.rect(screen, INNER, [50, 50, 305, 305])
    pygame.draw.rect(screen, WHITE, [50, 50, 305, 305], 5)
    pygame.draw.line(screen, WHITE, [50, 150], [350, 150], 5)
    pygame.draw.line(screen, WHITE, [50, 250], [350, 250], 5)
    pygame.draw.line(screen, WHITE, [150, 50], [150, 350], 5)
    pygame.draw.line(screen, WHITE, [250, 50], [250, 350], 5)


# 도형 그리기
def draw_figue():
    global x_values, y_values
    for i in range(3):
        for j in range(3):
            if i == 1:
                pygame.draw.line(screen, RED, [x_values[j] * 100 - 25, y_values[j] * 100 - 25], [x_values[j] * 100 + 25, y_values[j] * 100 + 25], 5)
                pygame.draw.line(screen, RED, [x_values[j] * 100 - 25, y_values[j] * 100 + 25], [x_values[j] * 100 + 25, y_values[j] * 100 - 25], 5)
            if i == 2:
                pygame.draw.circle(screen, GREEN, [x_values[j] * 100, y_values[j] * 100], 25, 5)



def main():
    pygame.init()
    pygame.display.set_caption("Tic Tac Toe")

    screen.fill(BLACK)
    draw_board()
    draw_figue()

    print(point)



    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if event.type == KEYDOWN:
                if event.key == K_ESCAPE:
                    pygame.quit()
                    sys.exit()


        pygame.display.update()

if __name__ == '__main__':
    main()



"""
0은 빈칸
1은 O
2는 X

800 600 사이즈 쯤으로 해서
3x3 필드로
누르면 0 -> 1 -> 2 -> 0
으로 변환되는 방식

vs 컴퓨터로
내가 한 번 누르면 컴퓨터가 한 번 누르는 턴 방식 게임

컴퓨터는 랜덤으로 아무 위치나 누르고
나는 O나 X를 한 줄 완성하면 되는 게임

업데이트 방향
    1. 
        나는 O를 한 줄, 컴퓨터는 X를 한 줄 만들기
    2. 
        한번 누르면 누른 위치는 한 번 진행, 다른 위치(예를 들면 십자나 엑스자 혹은 위아래, 좌우 등 몇 개의 위치)가 몇 번 진행되게
        그래서 O나 X를 한 줄 완성하기
"""

 

 

 

 

실제로 C#으로 틱택토를 구현하는 건 고난의 연속이였는데,

우선은 구현의 순서를 주석으로 구체화시킨 후에, 차례차례 구현해나가는 방식으로 진행했다.

1. 9 X 17의 맵 만들기

2. 맵에 구분선, 숫자 집어넣기

3. 맵을 로딩하기

4. UI(필요한 입출력 Console.WriteLine, Console.ReadLine) 위치, 디자인 정하기

5. 입력받은 값으로 맵을 다시 그리기 -> 함수 이용

6. 게임의 승패(플레이어 1 승리 or 플레이어 2 승리 or 무승부) 조건 만들기

7. 게임의 승패 여부 판단하기

의 순서로 구현해나갔다.

 

1의 맵 만들기부터 고난이였는데, 배열에 약했던 지식들을 총동원하여 맵을 만들었다.

이 과정에서 C#의 배열에 대해 좀 더 자신의 것으로 만들 수 있었던 계기가 된 것 같다.

 

이후의 내용들은 노가다 코딩으로 작업이 대부분 이루어졌는데,

맵에 숫자를 표시하거나, 플레이어의 입력 값에 따라 O와 X를 그리는 것,

플레이어들의 승리 조건, 무승부 조건을 대부분 노가다 코딩으로 작업했다.

아직은 C#에 대한 지식이 부족하여 좀 더 깔끔하고 아름답게 코딩하지 못한 것은 아쉽지만,

결과물은 잘 작동하는 것으로 보여 뿌듯함을 느꼈다.

 

using System;

class Program
{
    static void Main()
    {
        //int targetNumber = new Random().Next(1, 101);
        //int guess = 0;
        //int count = 0;
        //Console.WriteLine("1부터 100 사이의 숫자를 맞춰보세요");

        //while (guess != targetNumber)
        //{
        //    Console.Write("추측한 숫자를 입력하세요: ");
        //    guess = int.Parse(Console.ReadLine());
        //    count++;
        //    if (guess == targetNumber)
        //    {
        //        Console.WriteLine("축하합니다. 숫자를 맞췄습니다.");
        //        Console.WriteLine($"숫자는 {targetNumber}이었습니다.");
        //    }
        //    else if (guess > targetNumber)
        //    {
        //        Console.WriteLine("입력값이 큽니다.");
        //    }
        //    else
        //    {
        //        Console.WriteLine("입력값이 작습니다.");
        //    }
        //}
        
        //Console.WriteLine($"{count}번 시도하여 맞췄습니다.");



        // 틱택토
        // 2차원 배열을 사용하여 게임 맵을 구현

        int width = 17;
        int height = 9;
        char[,] map = new char[height, width];
        bool gameOverCheck = false;


        // 맵 초기화
        void Reset()
        { 
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    map[i, j] = ' ';
                }
            }

            // 구분선 넣기
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if ((i + 1) % 3 == 0 && i != height - 1)
                    {
                        map[i, j] = '_';
                    }
                    if ((j + 1) % 6 == 0)
                    {
                        map[i, j] = '|';
                    }
                }
            }

            map[1, 2] = '1';
            map[1, 8] = '2';
            map[1, 14] = '3';
            map[4, 2] = '4';
            map[4, 8] = '5';
            map[4, 14] = '6';
            map[7, 2] = '7';
            map[7, 8] = '8';
            map[7, 14] = '9';

        }

        // 입력값 표시
        void Drawing()
        {
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    Console.Write(map[i, j]);
                }
                Console.WriteLine();
            }
        }

        void PlayerChoice(char symbol)
        {
            string input = Console.ReadLine();


            if (input == "1" && map[1, 2] == '1')
            {
                map[1, 2] = symbol;
            }
            else if (input == "2" && map[1, 8] == '2')
            {
                map[1, 8] = symbol;
            }
            else if (input == "3" && map[1, 14] == '3')
            {
                map[1, 14] = symbol;
            }
            else if (input == "4" && map[4, 2] == '4')
            {
                map[4, 2] = symbol;
            }
            else if (input == "5" && map[4, 8] == '5')
            {
                map[4, 8] = symbol;
            }
            else if (input == "6" && map[4, 14] == '6')
            {
                map[4, 14] = symbol;
            }
            else if (input == "7" && map[7, 2] == '7')
            {
                map[7, 2] = symbol;
            }
            else if (input == "8" && map[7, 8] == '8')
            {
                map[7, 8] = symbol;
            }
            else if (input == "9" && map[7, 14] == '9')
            {
                map[7, 14] = symbol;
            }
            else
            {
                Console.WriteLine();
                Console.WriteLine("옳바른 값을 입력해주세요.");
                PlayerChoice(symbol);
            }
        }

        void IsFinish()
        {
            if ((map[1, 2] == 'X') && (map[1, 8] == 'X') && (map[1, 14] == 'X') ||
                (map[1, 2] == 'X') && (map[4, 2] == 'X') && (map[7, 2] == 'X') ||
                (map[1, 2] == 'X') && (map[4, 8] == 'X') && (map[7, 14] == 'X') ||
                (map[1, 8] == 'X') && (map[4, 8] == 'X') && (map[7, 8] == 'X') ||
                (map[1, 14] == 'X') && (map[4, 14] == 'X') && (map[7, 14] == 'X') ||
                (map[4, 2] == 'X') && (map[4, 8] == 'X') && (map[4, 14] == 'X') ||
                (map[7, 2] == 'X') && (map[7, 8] == 'X') && (map[7, 14] == 'X') ||
                (map[7, 2] == 'X') && (map[4, 8] == 'X') && (map[1, 14] == 'X')
                )
            {
                Console.WriteLine("플레이어 1의 승리");
                gameOverCheck = true;
            }
            else if ((map[1, 2] == 'O') && (map[1, 8] == 'O') && (map[1, 14] == 'O') ||
                (map[1, 2] == 'O') && (map[4, 2] == 'O') && (map[7, 2] == 'O') ||
                (map[1, 2] == 'O') && (map[4, 8] == 'O') && (map[7, 14] == 'O') ||
                (map[1, 8] == 'O') && (map[4, 8] == 'O') && (map[7, 8] == 'O') ||
                (map[1, 14] == 'O') && (map[4, 14] == 'O') && (map[7, 14] == 'O') ||
                (map[4, 2] == 'O') && (map[4, 8] == 'O') && (map[4, 14] == 'O') ||
                (map[7, 2] == 'O') && (map[7, 8] == 'O') && (map[7, 14] == 'O') ||
                (map[7, 2] == 'O') && (map[4, 8] == 'O') && (map[1, 14] == 'O')
                )
            {
                Console.WriteLine("플레이어 2의 승리");
                gameOverCheck = true;
            }
            else if ((map[1, 2] != '1') && (map[1, 8] != '2') && (map[1, 14] != '3') &&
                (map[4, 2] != '4') && (map[4, 8] != '5') && (map[4, 14] != '6') &&
                (map[7, 2] != '7') && (map[7, 8] != '8') && (map[7, 14] != '9')
                )
            {
                Console.WriteLine("무승부입니다.");
                gameOverCheck = true;
            }
        }

        Reset();

        Console.WriteLine("플레이어 1: X 와 플레이어 2: O");
        Console.WriteLine();
        Drawing();
        Console.WriteLine();

        while (true)
        {
            Console.WriteLine("플레이어 1의 차례");
            Console.WriteLine();
            Console.Write("1 ~ 9의 숫자를 입력하세요 : ");

            PlayerChoice('X');
            Drawing();
            IsFinish();
            if (gameOverCheck)
                break;
            Console.WriteLine();

            Console.WriteLine("플레이어 2의 차례");
            Console.WriteLine();
            Console.Write("1 ~ 9의 숫자를 입력하세요 : ");

            PlayerChoice('O');
            Drawing();
            IsFinish();
            if (gameOverCheck)
                break;
            Console.WriteLine();
        }
    }
}

 

 

게임을 모두 구현한 뒤에 발견했던 오류도 있었는데,

이미 플레이어가 선택한 숫자(현재 O또는 X로 표시되는 부분)를 다시 선택할 수 있는 오류를 발견하여

PlayerChoice(char symbol)의 if (input == "1") 부분을

if (input == "1" && map[1, 2] == '1')로 수정함으로써 해결할 수 있었다.

("1", "2", "3", ... , "9"까지 9개를 노가다로 수정했다)

 

과제를 제출한 뒤에 과제 풀이를 보니 훨씬 깔끔하고 간략하게 코딩한 것을 볼 수 있었는데,

아직은 지식이 부족하여 코드를 보아도 확실하게 이해하지 못하는 부분이 있었다.

내일 오전에 시간을 내서 해당 코드를 리뷰하며 차이점을 확인하고,

받아들일 수 있는 지식들을 최대한 받아들일 예정이다.