TIL(Today I Learned)

C# 문법 기초, 콘솔 숫자야구 게임 만들기 - TIL#7

Najdorf 2024. 1. 2. 20:26
728x90

오늘은 공부한 분량이 상당했기 때문에

전부 다 꼼꼼하게 정리하기엔 어느정도 한계가 있다고 판단했다...

 

그래서 공부할 때 메모했던 내용을 다듬는 식으로 정리할까 싶다.

 


항상 나는 Python을 주로 다뤘기 때문에

배열 = 리스트?

라고 단순히 생각했었다.

 

하지만, 적어도 C#에서는

배열과 리스트는 다르다!

 

배열은 처음에 정적으로 인덱스의 갯수를 할당해줘서

마치 단체 손님에게 방을 몇 개 내 줄 건가와도 같은 형식인데


리스트는 메모리가 동적 할당되며, 어느 부분에선 배열보다 간단하나,

당연히도 코딩 복잡도가 증가한다.

var 키워드 때처럼 무분별하게 사용하지 않는 것이 중요하다는 생각을 했다.

 

리스트와 기타 구조의 자료를 사용하고 싶다면
System.Collections.Generic 네임스페이스를 추가해서
List, Dictionary, Stack, Queue, HashSet을 쓸 수 있다!

 

 


항상 메서드가 무엇인가 의문이었다.

그렇지만, 메서드라는 것은 상당히 간단했다.

 

메서드는 내가 평소에 생각했던 함수다!

엄밀히 말하면 차이점이 있는 지는 모르겠지만,

적어도 내가 지금 이해하고 있는 바로는

메서드와 함수와 거의 동일한 것을 의미한다고 생각한다.

 

 

메서드는 오버로딩(Overloading) 이라는 것이 가능하다.

 

나는 함수와 같이 메서드도 이름 하나로만 정의하면

그 이상 정의를 해버리는 경우는 없을 것이라 생각했는데,

메서드의 파라미터(매개 변수)를 다르게 하는 방식으로 

다시 메서드를 정의할 수가 있다.

 

이걸 오버로딩이라고 한다.

 

목적은 파라미터의 형태에 따른 유연한 메서드 정의이다.

파라미터 자료형으로 int가 들어올 수도 있고 float이 들어올 수도 있는 가 하면

파라미터가 한 때는 2개만 필요했다가 3개가 필요해지는 순간이 올 수도 있는 것이다.

그래서 메서드를 오버로딩을 한다.

 

당장 Console.WriteLine도 오버로딩이 자료형에 맞춰서 18개나 되어있다.

 

주의할 점은,

반환값이 다른건 아무 의미 없다. 무조건 파라미터(매개변수)에서 차이가 있어야 한다.


그리고 공부를 하다가 Static 자료형에 관해 의문이 생겼는데,

이는 아래 링크를 참고하도록 하자. (아직은 전부 이해할 수 없다.)

https://antstudy.tistory.com/133

 

C# static(정적) 메서드

아래 글은 C# 문법 관련 개념 정리 및 작성자 공부를 위해 작성되었습니다. 📃 참고자료 https://www.csharpstudy.com/CSharp/CSharp-static.aspx https://wergia.tistory.com/180 https://ifcontinue.tistory.com/2 * 위 링크를 참

antstudy.tistory.com

 

 


2주차 과제로 '숫자 맞추기 게임' 이란 걸 할당받았다.

 

예제로 숫자 맞추기 게임을 풀어봤던 기억이 있는데,

진짜 말 그대로 숫자만 맞추는 게임이라

좀더 게임성이 있는 숫자 야구 게임을

콘솔 환경에서 작동하도록 구현해보기로 했다.

 

우선, 게임의 로직으로는

1~9까지의 숫자 4자리를 중복없이 랜덤으로 배정받고
사용자에게 4자리 숫자 입력받아서 비교한다.


그러면,
Strike, Ball, Out으로 세부 구분을 하게 된다.


Strike : 숫자가 맞고, 자리까지 정확히 들어맞음
Ball : 숫자는 4자리 중 있지만, 자리는 그 자리가 아님.
Out : 해당되는 숫자가 없음.

4자리 랜덤 숫자 -> Parse 한 다음, for 문으로 있는지 확인
-> Strike, Ball, Out 확인 -> 콘솔창으로 결과 출력

 

이런 순서로 로직을 짜면 구현이 되지 않을까 싶었다.

 

 

그러나... 입력받는 과정에서인지 오류가 발생했다.

 

Q1. 문제 발생 :
System.FormatException
  HResult=0x80131537
  메시지=The input string 'dd' was not in a correct format.
  소스=System.Private.CoreLib
  StackTrace:
   / System.Number.ThrowFormatException[TChar](ReadOnlySpan`1 value)
   / Systehttp://m.Int32.Parse(String s)
   / Program.Main(String[] args) 파일 C:\Users\user\source\repos\ConsoleApp1\ConsoleApp1\Program.cs:줄 30

 

아무래도 Console.ReadLine의 예외 처리 과정이 할당되지 않아서 뜨는 것 같은데,

내가 분명히 예외 처리 같은 걸 하지는 않았지만

지금까지 안해도 잘만 돌아갔었는데? 생각이 들었다.

그래서 굳이 지금와서 뭐가 다르다고 에러가 발생하나 괘씸하기도 했다.

 

허나...

A.
https://velog.io/@yiwonjin/C%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-17-%EC%98%88%EC%99%B8%EC%B2%98%EB%A6%AC
(처리되지 않은 예외의 발생과정 - 참고한 블로그)

 

저번에 짰던 거랑 확실히 다르게 짰긴 했나보다.

 

난 예제에서 했던 것과 같이 그대로 int.Parse(Console.ReadLine()) 과 같이

값을 받자마자 int형으로 변환해버리는 코드를 작성했는데,

요게 문제였던 것이다.


그래서 input 변수를 따로 정의하고,
input 변수에 ReadLine 메서드를 할당,

그리고 int.Parse(input)과 같은 꼴로 다시 짜니

 

그제서야 에러없이 정상작동하는 것을 볼 수 있었다.

 



Q2. 숫자 체크가 안됌:

 

이번엔 에러가 뜨지는 않지만

조건에 따라 Strike, Ball, Out으로 분류하는 로직이

전혀 작동이 안 된다는 것을 깨달았다.

 

여러가지 디버깅용 변수를 만들어서 출력해봤는데,
for문이 반복이 되는데 if 문에서 조건을 만족해도 참으로 받아 들이지 않는 것으로 확인했다.

 

이것도 머지 않아 문제점을 알게 되었다.

 


A. 이유?

int.Parse(input) 로직의 오류였다.
4자리 받아서 자릿수별로 변환해야 하는데
전체를 반복해서 넣어버린 것이다...

 

예를 들어, 8345라는 문자열을 입력헀는데

나는 이게 자리수별로 쪼개서 8, 3, 4, 5 각각

배열에 int형으로써 할당될 것을 기대했지만

 

당연하게도 저렇게 전체를 int형으로 변환하라고 했던 탓에

8345, 8345, 8345, 8345 이렇게 전체가 4번 할당되었던 것이다...

 

(역시 컴퓨터는 거짓말을 하지 않는다...)

 

이건 input을

input.Substring(i, 1) 로 바꾸는 것으로 해결했다.

 

Substring 메서드는 몇 번째 인덱스의 문자열부터 어느 길이의 문자열까지

슬라이싱을 할 건가에 대한 기능을 가지고 있다.

 

 

C#프로그래밍 17 : 예외처리

배열의 범위밖 인덱스 접근의 경우예외발생Array객체가 IndexOutofRangeException를 생성, 정보 적재IndexOutofRangeException이 Main으로 던져짐그러나 Main에 예외처리기가 없으므로 CLR로 다시 던져짐CLR에서 Unh

velog.io

 

아래가 완성된 전체 숫자 야구게임 스크립트다.

// 4자리 숫자 야구게임

internal class Program
{
    static void Main(string[] args)
    {
        Random random = new Random();
        int[] numbers = new int[4]; // 4자리 숫자 저장공간
        string homerun = "";

        // 4개의 랜덤 숫자 생성, 배열에 저장
        for (int i = 0; i < numbers.Length; i++)
        {
            int temp = random.Next(1, 10);
            // 중복되지 않는 선에서 숫자 추첨
            var check = Array.Exists(numbers, x => x == temp);
            if (check == false)
            {
                numbers[i] = temp;
                homerun += numbers[i].ToString();
            }
        }

        int attempt = 0;

        while (true)
        {
            Console.Write("4자리 숫자를 입력하세요.(1111~9999): ");
            int[] guesses = new int[4];

            int strike = 0;
            int ball = 0;
            int correct = 0;

            string input = Console.ReadLine();

            // 사용자가 입력한 숫자 배열에 저장
            for (int i = 0;i < guesses.Length; i++)
            {
                guesses[i] = int.Parse(input.Substring(i, 1));
            }

            // Strike, Ball 확인
            for (int i = 0; i < numbers.Length; i++)
            {
                for (int j = 0; j < guesses.Length; j++)
                {
                    if (numbers[i] == guesses[j])
                    {
                        correct++; // 숫자가 맞기만 하면 correct 값 증가
                        break;
                    }
                }

                if (numbers[i] == guesses[i])
                {
                    strike++;
                }
            }

            attempt++;
            ball = correct - strike; // 맞는 숫자 개수 중 strike가 아닌 값 산출

            Console.WriteLine("스트라이크: " + strike + "개, 볼: " + ball + "개, 아웃: " + (4 - correct) + "개, 시도 횟수: " + attempt);

            // 숫자를 맞출 경우 게임 종료
            if (strike == 4)
            {
                Console.WriteLine("홈런! 숫자를 맞추셨습니다! (시도 횟수 : " + attempt + ")");
                break;
            }
        }
    }
}

 


기타 메모:


Ctrl + L : 한 줄 삭제

 

개발자로서 갖춰야 할 능력
1. 문제 해결 능력
2. 커뮤니케이션 능력
중요한 건 협업과 기술적 고민
기술적 고민을 잘하려면, 근거가 있어야 함
체스의 수를 두는데 있어 근거가 필요한 거랑 비슷하다고 생각이 듦



배열에 값이 포함되어 있는지 확인하려면?

string value = "myValue";
var check = Array.Exists(myArray, x =? x == value);
if (check == true)
{
	// 실행할 코드
}


https://sungeun97.tistory.com/101

구조체 vs 클래스
구조체는 값 형식, 스택에 할당
클래스 참조 형식, 힙에 할당 참조로 전달
구조체는 상속 x, 클래스는 상속이 가능

생성자 평소엔 암묵적으로 되어있음.
생성자 오버로딩도 가능,
다만, 생성자를 명시하면 디폴트 생성자는 따로 생기지 않으니 주의.

프로퍼티 <- 요거 제대로 이해 못한 것 같음

상속 받을 때 이름이 같은 메서드를 호출한다면
해당 클래스에서 가장 가까운 메서드를 호출,
부모 클래스를 상속받은 자식 클래스에서 메서드 하나를 호출한다면,
자식 클래스의 메서드를 호출

virtual 키워드로 부모 클래스의 메서드를 재정의 가능
위에서 설명한 상속에서, 자식 클래스의 메서드와 가까운 것을 호출할 때와 다르게,
부모 클래스 자체를 사용해서 호출하는 경우,
각 자식 클래스에 해당하는 메서드를 호출할 수가 없음.
따라서 virtual 키워드를 사용. (부모 클래스에서 선언)
(자식이 재정의를 했을 수 있다 라는 여지를 남겨주는 키워드)

abstract 키워드로 상속받은 자식 클래스에게 무조건 메서드를 재정의 해야하는 강제성을 부여.

오버라이딩(Overriding)과 오버로딩(Overloading)은 다르다!
오버라이딩은 상속받은 자식이 매서드를 재정의하는 것.
오버로딩은 같은 메서드를 여러 번, 매개변수등을 다양화해서 정의하는 것.

제너릭:
클래스나 메서드를 일반화시켜서 다양한 자료형에 대응할 수 있는 기능
<T> 키워드를 이용해서 선언.

out, ref 키워드
본래 return이 하던 일을
매개변수가 할 수 있게 하는 느낌
out는 무조건 값이 바꼈겠구나 예상 가능
ref는 몰?루
사용하기 전 변수 값이 유지되지 않으니 주의해서 사용할 것.

 

 

[C#] 배열에 값을 포함하는지 확인하는 방법

1. Array.IndexOf() Array.IndexOf(array, element) 함수는 array 배열 내부의 요소에 element를 포함하면 해당 element의 index를 반환하고, 없으면 -1을 반환한다. using System; namespace check_element_in_array { class Program { stat

sungeun97.tistory.com

 

728x90