Coding/Python / 넘파이 하는 김에 빙고 만들기 신공.md

넘파이 하는 김에 빙고 만들기 신공

조회

쉬는시간에 시간 애껴서 만들었음… 아니 근데 솔직히 재밌어보이지 않습니까?


import numpy as np
import random

일단 이거 돌리려면 넘파이가 설치되어 있어야 한다. 일단 개발은 3.10에서 하긴 했는데 뭐 버전타는건 없겠지… 있으면 큰일난다… 나 3.10에서만 돌려봄…

 

# OOP 서타일은 클래스에서 시작이여
class Bingo:
    # 기본값은 5*5(기본 범위는 25)
    def __init__(self, size = 5, n = 25):
        self.size = size
        self.bingo_max = n
        self.bingo_range = range(1, self.bingo_max + 1)
        self.board = None
        self.number_list = list(self.bingo_range)

    # 빙고판 생성
    def make_bingo(self): 
        self.board = np.random.choice(self.bingo_range, size=(self.size, self.size), replace=False)
    
    # 숫자 뽑기
    def draw_number(self): 
        if len(self.number_list) > 0:
            bingo_pop = random.choice(self.number_list)
            self.number_list.remove(bingo_pop)
            return bingo_pop
        else: 
            print('No more draw!')
            return None
    
    # 빙고 체크 함수
    def isbingo(self, n = 1):
        count = 0
        # 줄 
        count += np.sum(np.all(self.board == 0, axis=1))
        count += np.sum(np.all(self.board == 0, axis=0))
        count += np.all(np.diag(self.board) == 0) # 대각선(좌상-우하)
        count += np.all(np.diag(np.fliplr(self.board)) == 0) # 대각선(우상-좌하)
        # 빙고가 됐어? 
        return count >= n
    # 마킹
    def marking(self, jebi):
        if jebi in self.board:
            self.board[self.board == jebi] = 0
            return True
        return False

    # 출력 관련임다. 깔끔하게 뽑을라고... 
    def print_board(self):
        for row in self.board:
            print(" ".join(f"{num:2}" for num in row))
        print("+-----------------------+")

OOP 서타일의 완성은 모든걸 다 클래스에 때려박아서 각 역할별로 각 메소드가 책임지게 만드는 것이다. 물론 처음에는 클래스 없이 절차지향으로 했던거 저기다가 다 때려박는 정말 개같은 난이도가 날 기다리고 있었지만… 그래서 쟤들이 빙고를 할 때 필요한 것들을 다 할 거다.

 

# 기본값은 5*5(기본 범위는 25)
def __init__(self, size = 5, n = 25):
    self.size = size
    self.bingo_max = n
    self.bingo_range = range(1, self.bingo_max + 1)
    self.board = None
    self.number_list = list(self.bingo_range)

생성자다. 여기서는 빙고의 크기와 빙고게임 숫자 범위(1부터 n까지)를 정하고 보드와 뽑기통을 만든다.

 

# 빙고판 생성
def make_bingo(self): 
    self.board = np.random.choice(self.bingo_range, size=(self.size, self.size), replace=False)

# 숫자 뽑기
def draw_number(self): 
    if len(self.number_list) > 0:
        bingo_pop = random.choice(self.number_list)
        self.number_list.remove(bingo_pop)
        return bingo_pop
    else: 
        print('No more draw!')
        return None

이건 간단하니까 두개 같이 설명드림. 위에껀 생성자에서 받은 숫자 범위로 빙고판을 만드는거고(1부터 50까지를 입력하면 1부터 50까지 중에서 랜덤으로 숫자를 뽑아 n*n 정사각형 배열에 들어갈 빙고판을 만든다), 아래는 위에 만들어진 뽑기통에서 숫자를 뽑는거다.

 

# 빙고 체크 함수
def isbingo(self, n = 1):
    count = 0
    # 줄 
    count += np.sum(np.all(self.board == 0, axis=1))
    count += np.sum(np.all(self.board == 0, axis=0))
    count += np.all(np.diag(self.board) == 0) # 대각선(좌상-우하)
    count += np.all(np.diag(np.fliplr(self.board)) == 0) # 대각선(우상-좌하)
    # 빙고가 됐어? 
    return count >= n

그니까 우리가 마킹하다가 가로세로대각선 한줄 지워지면 빙고잖아요? 그걸 판별하는 메소드다. 근데 diag는 뭐냐고? 주대각선이요. 행렬의 좌상->우하로 가는 대각선이 주대각선이고 밑에건 거꾸로 뒤집는거다. 플립 들어갔을 때 알아봄.

 

 

# 마킹
def marking(self, jebi):
    if jebi in self.board:
        self.board[self.board == jebi] = 0
        return True
    return False

마킹하는 애다. 근데 리턴트류 리턴폴스는 왜 있냐고? 보드가 5*5인데 숫자가 1부터 50까지면 보드에 안 들어가는 숫자도 있잖아요? 그럴 때 폴스가 뜬다.

 

# 출력 관련임다. 깔끔하게 뽑을라고... 
def print_board(self):
    for row in self.board:
        print(" ".join(f"{num:2}" for num in row))
    print("+-----------------------+")

얘는 뭐 크게 볼 건 없고 출력 관련이다. 아니 이게 언패킹을 했더니 개같이 안이쁜거야... 그렇다고 []를 뗄 수는 없잖아요... 그런거임. 저것도 숫자 3~4자리 되면 num 뒤에 숫자 바꿔야 할 수도 있음. 아니면 범위를 1~99로 제한하던가...

 

# 빙고 보드 생성 
while True: 
    bingo_num = int(input('Write maximum number: '))
    size = 5 # 여기 바꾸시면 빙고 크기 바껴요! (기본 5*5)
    try: 
        if bingo_num < size ** 2:
            raise ValueError
        else: 
            game = Bingo(size, bingo_num)
            break
    except ValueError:
        print(f'Out ot range: You have to input number larger than {size ** 2}')

# 목표 설정
while True:
    max_line = 2 * size + 2
    bingo_line = int(input('How many lines for end game?: '))
    try: 
        if bingo_line >= 1 and bingo_line <= max_line: 
            break
        else: 
            raise ValueError
    except ValueError:
        print(f'Out ot range: You have to input number between 1 and {max_line}')

보드 생성하고 목표 설정에는 트롤링 방지 장치가 있어서 긴거고 나머지는 심플하다. 숫자 범위의 경우 5*5로 해놓고 범위가 25보다 작으면 넘파이가 이건 뭐 배열을 뭐 어쩌라는겨 하면서 오류를 토함. 그래서 그거 관련으로 준 거고... 그 빙고판의 크기에 따라서 빙고 줄 수 최댓값 있는거 아셨음? 나도 인제 알았음. 가로+세로+대각선 해서 사이즈 * 2 + 2가 최대다. 그리고 0줄은 있을 수 없으니까 1부터 최댓값까지 제한을 거는 것.

 

# 빙고판 생성
game.make_bingo()

# 몇트째임?
bingo_cnt = 1

# 이 숫자가 빙고판에 있으면 지우고, 없으면 패스. 
# 일단 가로세로대각선 1빙고 될 때까지 해봅시다. 
while True: 
    # 랜덤으로 숫자 하나를 뽑는다. (범위는 위에 빙고판 숫자랑 동일)
    bingo_jebi = game.draw_number()
    # 물론 그럴 일은 없겠지만 리스트 거덜나면 끝납니다. 
    if bingo_jebi is None:
        print("No more draw! Game over.")
        break

    # 체크(위에 있음)
    if game.marking(bingo_jebi):
        print(f'{bingo_cnt}: {bingo_jebi} hit! 👏')
    else: 
        print(f'{bingo_cnt}: {bingo_jebi} no hits... 😭')
    game.print_board()

    # 조건 ㅇㅋ->시마이
    bingo_cnt += 1
    if game.isbingo(bingo_line):
        print(f'🎉Congratulations!')
        break

나머지는 판 만들고 몇판째인지 세고 빙고게임 하는게 다임. 뽑기통 거덜나면 끝나는 로직도 넣어두긴 했는데 그 전에 빙고게임이 끝나겠지 지피티야…

'Coding > Python' 카테고리의 다른 글

의도와 많이 달라졌지만 Python으로 ARIMA를 해보자  (0) 2025.12.26
loc, iloc, at, iat  (0) 2025.12.24
힙 정렬을 구현해보자  (0) 2025.12.13
BFS, DFS  (0) 2025.12.11
이중 연결 리스트  (0) 2025.12.10

댓글

홈으로 돌아가기

검색 결과

"search" 검색 결과입니다.