728x90
반응형

기본적인 BFS, DFS 문제이다. (N, N) 좌표에 도착할 수 있는지를 확인해주면 된다.

BFS 풀이

from collections import deque

def bfs(y, x):
    q = deque()
    q.append((y, x, li[y][x]))
    while q:
        y, x, d = q.popleft()
        for i, j in [(1, 0), (0, 1)]:
            Y, X = y + d*i, x+ d*j
            if 0 <= Y < N and 0 <= X < N and d != 0:
                if Y == X == N-1:
                    return True
                q.append((Y, X, li[Y][X]))
    return False
        
N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]    
print("HaruHaru" if bfs(0, 0) else "Hing")

DFS 풀이

import sys
sys.setrecursionlimit(10000)

def dfs(y, x):
    global ok
    d = li[y][x]
    for i, j in [(1, 0), (0, 1)]:
        Y, X = y + d*i, x+ d*j
        if 0 <= Y < N and 0 <= X < N and d != 0:
            if Y == X == N-1:
                ok = 1
                return ;
            dfs(Y, X)
        
N = int(input())
li = [list(map(int, input().split())) for _ in range(N)]
ok = 0
dfs(0, 0)
print("HaruHaru" if ok else "Hing")
728x90
반응형
728x90
반응형

이번엔 vscode와 원격 서버를 연결해보자. 생각보다 간단하다.

 

Extensions에서 Remote Development를 설치하고

 

ctrl + shift + p를 눌러 Command Palette를 열고 Remote-SSH: Connect to Host를 실행한 후

Add New SSH Host -> ssh 계정@ip주소or도메인 -> 비밀번호를 입력하면 서버에 접속된다.

 

이렇게 접속은 됐는데 뭐만 하면 계속 비밀번호 입력하라 하고 되게 귀찮게 하는데

내 로컬 컴퓨터의 보안키 파일을 서버에 옮기고 vscode 설정을 조금만 해주면 해결된다.

 

우선 키를 발급하자. cmd에다 ssh-keygen -t rsa -b 4096 명령어를 실행하고 엔터 몇 번 누르면

아래와 같이 키(id_rsa.pub)가 발급된다. 나는 이미 발급해서 저렇게 나왔지만 원래는 이상한 이미지가 나온다!

 

scp를 사용해서 서버에 id_rsa.pub파일을 보내야 되는데 아래와 같은 방식으로 명령어를 입력해주면 된다.

scp -P [서버 포트] [전송 파일] [서버 계정]@[서버 아이피]:[파일 저장 경로]

EX) scp -P 포트 .ssh/id_rsa.pub jinho@~.~.~.~:.ssh/

 

이제 서버에서 .ssh 경로로 들어간 다음 아래 명령어를 입력해주고 

cat id_rsa.pub >> .ssh/authorized_keys


vscode로 넘어와 ctrl + shift + p -> Remote-SSH: Open SHH Configuration File 

-> .ssh 폴더의 config 파일을 열어주자.

 

그 후 아래와 같은 방식으로 config 파일을 작성해주면 끝이다.

Host, HostName: ip주소

User: 계정 이름

Port: 포트 번호

IdentifyFile: ~/.ssh/id_rsa

 

이제 vscode를 껐다 켜도 비밀번호를 물어보지 않고 바로 접속이 된다!!

728x90
반응형

'설치, 삽질, 마크다운 등등' 카테고리의 다른 글

vscode 배경 이미지 넣기  (0) 2021.10.08
마크다운 정리  (0) 2021.03.05
728x90
반응형

훈련소 갔다 오고 나서 오랜만에 글을 쓴다.

최근에는 주피터 노트북에서 벗어나 vscode를 사용해보고 있는 중이다. 서버 접속도 뚝딱이고 아주 편리한 것 같다.

 

오늘은 vscode에 배경 이미지를 추가해보자.

Extensions에서 background를 설치하고

 

ctrl + shift + p를 눌러 Command Palette를 열고 Preferences: Open Settings (JSON)를 검색해서 

settings.json파일을 연다. 그러면 아래와 같이 뭔가 잡다하게 적혀있거나 아예 비어있거나 둘 중 하나일 건데

 

{} 안에 아래 코드를 추가해주면 

    "background.enabled": true,
    "background.loop": false,
    "background.useDefault": false,
    "background.useFront": true,
    "background.style": {
        "content": "''",
        "pointer-events": "none",
        "position": "absolute",
        "z-index": "99999",
        "width": "100%",
        "height": "100%",
        "background-position": "left",
        "background-size": "cover",
        "background-repeat": "no-repeat",
        "opacity": 0.25
    },
    "background.customImages": [
        "https://media.vlpt.us/images/wjdgml3834/post/04747625-84d3-4d6e-a940-5e00b73babf7/image.jpeg"
    ]

 

이런 느낌으로 배경이 적용된다. background.style에서 스타일을 바꿀 수 있고

background.customImages에서 배경으로 쓰고 싶은 이미지를 지정해줄 수 있다.

728x90
반응형
728x90
반응형

기본적인 그래프 탐색 문제이다. 

각 노드별로 몇 명의 사람을 만날 수 있는지 확인하고, 최댓값의 인덱스를 출력해주면 된다 -> res.index(max(res))

def dfs(node, cnt):
    check[node] = 1
    n = graph[node][0]
    if check[n] == 0:
        cnt = dfs(n, cnt+1)
    return cnt

N = int(input())
graph = [[] for _ in range(N+1)]
res = [0]*(N+1)
for u in range(1, N+1):
    v = int(input())
    graph[u].append(v)
for i in range(1, N+1):
    check = [0]*(N+1)
    res[i] = dfs(i, 1)
print(res.index(max(res)))
728x90
반응형
728x90
반응형

브루트포스 알고리즘 & 백트래킹 문제이다.

나누기 처리를 할 때 int(n1 / n2) 또는 -(-n1 / n2) 같은 방식으로 나눠야 하는데,

처음에 이걸 몰라서 계속 틀렸다.

def dfs(res, i, add, sub, mul, div):
    global N
    if i == N:
        res_li.append(res)
    else:
        if add:
            dfs(res + nums[i], i+1, add-1, sub, mul, div)            
        if sub:
            dfs(res - nums[i], i+1, add, sub-1, mul, div)
        if mul:
            dfs(res * nums[i], i+1, add, sub, mul-1, div)
        if div:
            dfs(int(res / nums[i]), i+1, add, sub, mul, div-1)          
    
N = int(input())
nums = list(map(int, input().split()))
add, sub, mul, div = map(int, input().split())
res_li = []

dfs(nums[0], 1, add, sub, mul, div)
print(max(res_li))
print(min(res_li))
728x90
반응형
728x90
반응형

나도 그랬었지만 처음 코딩을 해보는 사람이라면 가상환경이 왜 필요할까? 싶을 수도 있다. 

하나 똑같은 환경에서 여러 프로젝트 작업을 하다 보면 같은 라이브러리여도 어느 프로젝트에서는

다른 버전을 사용해야 되거나 라이브러리간 충돌이 일어난다는 등등 여러 문제가 있을 수도 있다.

그렇기에 진작에 프로젝트를 시작할 때 따로 가상환경을 만들어 놓고 거기에서 작업을 하는 게 편하다.

 

파이썬에서 가상환경을 만드는 방법은 되게 많다. conda, virtualenv, venv 등등이 있는데

이번엔 virtualenv와 venv로 가상환경을 구축해보겠다.

둘의 차이는 거의 없는데 venv는 python3에서만 가능한 것 같다.

 

virtualenv

아래 명령어를 통해 virtualenv를 설치해준 후

sudo apt install virtualenv

아래 명령어를 실행하면 env1이라는 이름의 가상환경이 생성된다.

virtualenv env1

env1 폴더가 생성된 것을 확인할 수 있는데, 위 명령어로 활성화, 아래 명령어로 비활성화가 가능하다.

source env1/bin/activate # 활성화
deactivate # 비활성화

 

파이썬 버전을 지정하고 싶다면 명령어 뒤에 --python=python3.7 같은 식으로 추가해주면 된다. 

가상환경 삭제는 sudo rm -rf venv 같은 방식으로 가능하다. 

 

venv

아래 명령어를 실행하면 env2이라는 이름의 가상환경이 생성된다.

python3 -m venv env2

활성화, 비활성화 방식은 virtualenv와 똑같다.

 

+ 추가

해당 환경에서 설치한 라이브러리들을 저장하고 싶다면 pip freeze를 사용하자.

pip freeze > requirements.txt

아래와 같이 requirements.txt 파일이 생성되고, 파일을 확인해보면 설치한 라이브러리들이 저장되어 있다.

 

위에서 생성한 requirements.txt를 사용하면 다른 환경에서도 그대로 라이브러리 설치가 가능하다.

pip install -r requirements.txt
728x90
반응형
728x90
반응형

아래 주소에서 exe파일을 다운 받아 VNC Viewer를 설치

https://www.realvnc.com/en/connect/download/viewer/

 

Download VNC Viewer | VNC® Connect

VNC® Connect consists of VNC® Viewer and VNC® Server Download VNC® Viewer to the device you want to control from, below. Make sure you've installed VNC® Server on the computer you want to control. Frequently asked questions How do I install VNC® View

www.realvnc.com

 

라즈베리파이 터미널에서 ifconfig 명령어를 통해 ip를 확인. wlan0 -> inet 

 

VNC Viewer를 실행해서

ip 검색 후

 

Username과 Password를 입력하면 

 

라즈베리파이에 접속이 된다!

 

728x90
반응형
728x90
반응형

라즈베리파이를 설치하고 처음으로 코드를 짜려고 하면 매우 답답할 수밖에 없다. 

코드가 메모장에 있는 텍스트들 마냥 똑같이 보이기 때문이다.

이번엔 vim을 설치한 후에 vimrc파일을 수정하여 위 문제를 해결해보도록 하자.

 

첫 번째 명령어로 vim을 설치하고 두 번째 명령어로 vimrc파일을 열어주자.

sudo apt-get install vim 
vi ~/.vimrc

그 후 아래 내용을 vimrc파일에 입력하고 저장해주면 코드들이 전보다 보기 훨씬 좋아진다! 

set number
set ai
set si
set cindent
set shiftwidth=4
set tabstop=4
set ignorecase
set hlsearch
set expandtab
set background=dark
set nocompatible
set fileencodings=utf-8,euc-kr
set bs=indent,eol,start
set history=1000
set ruler
set nobackup
set title
set showmatch
set nowrap
set wmnu

syntax on

 

 여기까지는 직접 설정을 해본 것이고 github를 찾아보니 vimrc 설정 끝판왕이 있는 것 같다.

명령어 두줄이면 바로 적용된다. 만약 git이 안 깔려있다면 sudo apt install git 명령어로 설치해주자.

git clone --depth=1 https://github.com/amix/vimrc.git ~/.vim_runtime
sh ~/.vim_runtime/install_awesome_vimrc.sh

https://github.com/amix/vimrc

 

GitHub - amix/vimrc: The ultimate Vim configuration (vimrc)

The ultimate Vim configuration (vimrc). Contribute to amix/vimrc development by creating an account on GitHub.

github.com

 

적용하면 아래와 같이 매우 편--안하게 코드를 볼 수 있다.

편--안

728x90
반응형
728x90
반응형

우선 라즈베리파이 본체, sd카드, sd카드 리더기, 모니터, 키보드, 마우스가 필요합니다.

없으면 구해오도록 합시다!

 

라즈베리파이 설치

다 있다면 아래 주소에서 Win32 Disk Imager Installer와 Raspberry Pi OS를 다운로드  

Raspberry Pi OS

https://softfamous.com/win32-disk-imager/

 

Win32 Disk Imager - Soft Famous

It is recommended to keep backups of important files. It is easier to make a full backup of a disk or external storage device. This tool helps you to achieve it. When you want to create a duplicate file from

softfamous.com

https://www.raspberrypi.org/software/operating-systems/

 

Operating system images – Raspberry Pi

The Raspberry Pi is a tiny and affordable computer that you can use to learn programming through fun, practical projects. Join the global Raspberry Pi community.

www.raspberrypi.org

 

Win32 Disk Imager Installer를 실행하여 설치(동의하고 계속 다음을 눌러주자)

sd카드를 sd카드 리더기를 연결 후 노트북에 연결 

Win32 Disk Imager를 실행하여 연결한 sd카드에 Raspberry pi OS를 Wirte 해줍니다.

 

한몇 분? 정도 지나면 설치가 완료됩니다.

 

그 라즈베리파이에 충전기, sd카드, 마우스, 키보드, 모니터를 모두 연결해줍니다. 

그러면 딱 아래와 같은 화면이 모니터에 나오게 됩니다.

 

라즈베리파이 설정

저는 아래와 같이 설정했더니 다 잘됐습니다! 만약 안된다면 무한 구글링을 해보시기 바랍니다.

Menu -> Preferences -> Raspberry Pi Configuration을 열어봅시다.

 

System에서 Password와 Hostname을 변경해줍시다. 필수는 아님!

 

Interfaces에서 사용할 기능들은 Enable 해줍시다. Camera, SSH, VNC 같은 애들은 Enable 해놓도록 하자.

 

이제 사실상 제일 중요한 부분인 Localisation을 설정해보자. 대부분 여기서 삽질을 제일 많이 하는 것 같다.

Locale에서 Language: en(English), Country: GB (United Kingdom), Character set: UTF-8

Timezone은 자기 마음대로!

Keyboard에서 Model: Generic 105-key PC (intl.), Layout: Korean, Variant: Korean (101/104 key compatible)


WiFi Country Code에서 Country: GB Britain (UK) 

 

Configuration 설정을 다했다면 reboot를 해주고 터미널에 아래 명령어를 실행시켜 주면 끝이다!

sudo apt update
sudo apt upgrade
sudo apt install -y fonts-unfonts-core
sudo apt install ibus-hangul
728x90
반응형
728x90
반응형

begin에서 target으로 가는 최단경로의 길이를 찾는 문제이다.

두 단어의 차이가 한 글자면 이동 가능하다. 이동 가능한지 판별하는 부분은 아래의 조건문같이 구현했다.

if len([1 for i in range(N) if node[i] != n[i]]) != 1:

 

dfs를 사용해서 이동 가능한 모든 경로를 다 돌아보고 현재 node가 target이라면

res리스트에 지나왔던 경로의 길이를 append해준다. 마지막에 min(res)를 반환해주면 끝이다.

def solution(begin, target, words):
    def dfs(cnt, node, li, route):
        if node == target:
            res.append(cnt)
        for i, n in enumerate(li):
            if len([1 for i in range(N) if node[i] != n[i]]) != 1:
                continue
            li.pop(i)
            dfs(cnt+1, n, li, route+[n])
            li.insert(i, n)
            
    if target not in words:
        return 0
    
    N = len(begin)
    res = []
    dfs(0, begin, words, [begin])
    
    return min(res) if res else 0
728x90
반응형
728x90
반응형

간단한 그래프 탐색 문제이다. 반복문을 돌면서 체크가 안된 노드가 있다면 ans += 1을 하고

그 노드부터 시작하여 dfs 또는 bfs를 돌려서 갈 수 있는 길을 다 체크해주면 된다.

check[i] = 0 -> i 노드 들린 적 없음 -> 탐색 시작

check[i] = 1 -> i 노드 들린 적 있음 -> 탐색 X 

보아하니 백준 실버 4 ~ 실버 2 정도가 프로그래머스 Level 3이랑 비슷한 것 같다.

 

DFS 풀이

def solution(n, computers):
    def dfs(node):
        if graph[node] and check[node] == 0:
            check[node] = 1
            for n in graph[node]:
                dfs(n)
    
    graph = [[]]
    for i in range(n):
        graph.append([j+1 for j in range(n) if computers[i][j] == 1 and i != j])
    
    check = [0]*(n+1)
    ans = 0
    for i in range(1, n+1):
        if check[i] == 0:
            dfs(i)
            ans += 1

    return ans

 

BFS 풀이

from collections import deque

def solution(n, computers):
    def bfs(node):
        q = deque([node])
        while q:
            node = q.popleft()
            if graph[node] and check[node] == 0:
                check[node] = 1
                for n in graph[node]:
                    q.append(n)
    
    graph = [[]]
    for i in range(n):
        graph.append([j+1 for j in range(n) if computers[i][j] == 1 and i != j])
    
    check = [0]*(n+1)
    ans = 0
    for i in range(1, n+1):
        if check[i] == 0:
            bfs(i)
            ans += 1

    return ans
728x90
반응형
728x90
반응형

리스트가 아닌 딕셔너리로 그래프를 구현해서 풀어야되는 문제이다.

올바른 경로를 찾을 때까지 뺑뺑이를 돌려줘야 되는 문제라 dfs로 풀었다. 

핵심은 dfs 함수를 재귀로 넘기기 전에 딕셔너리의 value값을 삭제하고 재귀후에 다시 추가해주는 부분같다.

처음에 이 부분을 잘못 생각해서 푸는데 오래걸렸다... 

from collections import defaultdict

def dfs(N, graph, route, node):
    if len(route) == N+1:
        return route
    for i, n in enumerate(graph[node]):
        graph[node].pop(i)
        ret = dfs(N, graph, route+[n], n)
        graph[node].insert(i, n)
        if ret:
            return ret
        
def solution(tickets):
    N = len(tickets)
    graph = defaultdict(list)
    for s, e in tickets:
        graph[s].append(e)
    for k in graph:
        graph[k].sort()
    
    route = dfs(N, graph, ["ICN"], "ICN") 
    return route

 

추가로 defaultdict라는 친구에 대해 알게되었다.

여태까지 딕셔너리에 값을 채울 때나 키값이 있는지 조건문으로 확인할 때 get을 사용해서 따로 처리하곤

했었는데, defaultdict를 사용하면 오류 걱정없이 그냥 넣어주고 확인하면 된다! 

728x90
반응형
728x90
반응형

순위를 정확하게 매길 수 있는 사람이 몇 명인지 찾는 문제이다.

플로이드 와샬 알고리즘을 모른다면 풀기 조금 힘들 것 같은 문제이다. 

플로이드 와샬을 사용해서 최단경로가 아닌 승패를 확인해주면 된다.  

EX)

graph[0][1] = 1 -> 1가 2를 이김

graph[4][3] = -1 -> 4가 5를 이김

graph[1][2] = 0 -> 2와 3의 승패를 알 수 없음 

def solution(n, results):
    graph = [[0]*n for i in range(n)]
    for a, b in results:
        graph[a-1][b-1] = 1
        graph[b-1][a-1] = -1
        
    for k in range(n):
        for i in range(n):
            for j in range(n):
                if graph[i][k] == 1 and graph[k][j] == 1:
                    graph[i][j] = 1
                if graph[i][k] == -1 and graph[k][j] == -1:
                    graph[i][j] = -1
    
    ans = 0
    for li in graph:
        if li.count(0) == 1:
            ans += 1
    return ans
728x90
반응형
728x90
반응형

1번 노드에서 제일 먼 노드가 몇 개 인지 찾는 문제이다. 

BFS로 아주 쉽게 풀 수 있다.

from collections import deque

def solution(N, edge):
    graph = [[] for _ in range(N+1)]
    for a, b in edge:
        graph[a].append(b)
        graph[b].append(a)
    
    q = deque([1])
    check_li = [-1]*(N+1)
    check_li[1] = 0
    while q:
        node = q.popleft()
        for n in graph[node]:
            if 0 < n <= N and check_li[n] == -1:
                q.append(n)
                check_li[n] = check_li[node] + 1
                    
    ans = check_li.count(max(check_li))
    return ans
728x90
반응형
728x90
반응형

mimi_s.ico
0.12MB

이번엔 저번에 구현했던 미로 게임을 나름 업그레이드해봤다.

https://jinho-study.tistory.com/1081

 

기본적인 게임 개발 기술(실시간 처리, 키 입력 받기, 미로 게임)

이번엔 tkinter를 사용해서 기본적인 게임 개발 기술(실시간 처리, 키 입력 받기, 미로 게임)에 대해 알아보자. 1 실시간 처리 구현하기 파이썬에서는 after() 명령으로 실시간 처리를 수행할 수 있다

jinho-study.tistory.com

 

전체 코드는 아래와 같다.

import tkinter
import tkinter.messagebox


mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
state = 0 # 게임 상황, 0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능
key = 0 # 키 이름을 입력할 변수 선언

# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입
    
# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

# 캐릭터 이동 함수
def move():
    global mx, my
    
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    
    # 캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    # 칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        # PAINT(지났던 길) 태그 추가
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0, tag="PAINT")
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동

# 칠해지지 않은 칸 수를 세주는 함수    
def count_tile():
    cnt = 0
    for i in range(7):
        for j in range(10):
            if maze[i][j] == 0:
                cnt += 1
    return cnt
    
# 게임 상태 확인 함수    
def check():
    cnt = count_tile()
    
    # 게임 클리어 불가능
    if 0 not in [maze[my-1][mx], maze[my+1][mx], maze[my][mx-1], maze[my][mx+1]]:
        return 2
    # 게임 클리어
    elif cnt == 0:
        return 1
    # 게임 진행
    else:
        return 0
        
# 게임 초기화 함수
def reset():
    global mx, my, state
    state = 0
    canvas.delete("PAINT")
    mx = 1
    my = 1
    for y in range(7):
        for x in range(10):
            if maze[y][x] == 2:
                maze[y][x] = 0
                
# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, state, key
        
    # Esc 키를 누를 시 게임 종료
    if key == "Escape":
        key = 0
        ret = tkinter.messagebox.askyesno("종료", "게임을 종료하시겠습니까?")
        if ret == True:
            root.destroy()
            return ;
        
    # 왼쪽 Shift 키를 눌렀고 미로가 2칸 이상 칠해진 상태라면 게임 초기화
    if key == "Shift_L":
        reset()
                            
    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()
    
    root.after(100, main_proc)

    
root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

# 미로 생성
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file="mimi_s.png")
# MYCHR(캐릭터) 태그 추가
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

 

 

게임 종료와 다시 시작 관련해서 기능을 추가하고 구조를 조금 수정했다. 

  • 0.2초 딜레이는 뭔가 답답해서 0.1초로 줄였다.
  • 게임을 클리어한 후에 Shift 키를 누르지 않아도 자동으로 게임을 초기화하고 다시 시작한다.
  • Esc 키를 입력받으면 yesorno 메시지 박스를 표시해주고 yes면 destroy() 명령을 사용해 게임을 종료시킨다.
  • yuka변수를 없애고 reset(게임 초기화), check(게임 상태 확인), count_tile(칠해지지 않은 칸 수 확인) 함수를 추가했다.
  • state 변수를 사용해서 게임 진행을 관리하는 식으로 메인 함수 구조를 수정했다.
    0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능

 

게임 진행 관리 부분 코드

    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()

 

pyinstaller용 코드와 명령어

전체 코드

import tkinter
import tkinter.messagebox
import os


def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

mx = 1 # 캐릭터의 가로 뱡향 위치를 관리하는 변수
my = 1 # 캐릭터의 세로 뱡향 위치를 관리하는 변수
state = 0 # 게임 상황, 0: 게임 진행, 1: 게임 클리어, 2: 게임 클리어 불가능
key = 0 # 키 이름을 입력할 변수 선언

# 키를 눌렀을 때 실행할 함수 정의
def key_down(e):
    global key # key을 전역 변수로 취급
    key = e.keysym # 눌려진 키 이름을 key에 대입
    
# 키를 눌렀다 뗐을 때 실행할 함수 정의
def key_up(e):
    global key # key을 전역 변수로 취급
    key = "" # key에 빈 문자열 대입

# 캐릭터 이동 함수
def move():
    global mx, my
    
    # key 방향이 통로라면 그 방향에 맞게 mx, my값을 변경
    if key == "Up" and maze[my-1][mx] == 0: 
        my -= 1
    if key == "Down" and maze[my+1][mx] == 0:
        my += 1
    if key == "Left" and maze[my][mx-1] == 0:
        mx -= 1
    if key == "Right" and maze[my][mx+1] == 0:
        mx += 1    
    
    # 캐릭터가 있는 장소가 벽이 아니라면 리스트 값을 2로 변경,
    # 칠한 회수를 1 증가시키고, 해당 위치를 분홍색으로 칠한다.
    if maze[my][mx] == 0:
        maze[my][mx] = 2
        # PAINT(지났던 길) 태그 추가
        canvas.create_rectangle(mx*80, my*80, mx*80 + 79, my*80 + 79, 
                                   fill="pink", width=0, tag="PAINT")
    canvas.delete("MYCHR")
    canvas.create_image(mx*80 + 40, my*80 + 40, image=img, tag="MYCHR") # 캐릭터 이미지 이동

# 칠해지지 않은 칸 수를 세주는 함수    
def count_tile():
    cnt = 0
    for i in range(7):
        for j in range(10):
            if maze[i][j] == 0:
                cnt += 1
    return cnt
    
# 게임 상태 확인 함수    
def check():
    cnt = count_tile()
    
    # 게임 클리어 불가능
    if 0 not in [maze[my-1][mx], maze[my+1][mx], maze[my][mx-1], maze[my][mx+1]]:
        return 2
    # 게임 클리어
    elif cnt == 0:
        return 1
    # 게임 진행
    else:
        return 0
        
# 게임 초기화 함수
def reset():
    global mx, my, state
    state = 0
    canvas.delete("PAINT")
    mx = 1
    my = 1
    for y in range(7):
        for x in range(10):
            if maze[y][x] == 2:
                maze[y][x] = 0
                
# 실시간 처리를 수행할 함수 정의
def main_proc():
    global mx, my, state, key
        
    # Esc 키를 누를 시 게임 종료
    if key == "Escape":
        key = 0
        ret = tkinter.messagebox.askyesno("종료", "게임을 종료하시겠습니까?")
        if ret == True:
            root.destroy()
            return ;
        
    # 왼쪽 Shift 키를 눌렀고 미로가 2칸 이상 칠해진 상태라면 게임 초기화
    if key == "Shift_L":
        reset()
                            
    state = check()
    # 게임 진행
    if state == 0:
        # 캐릭터 이동
        move()
    # 클리어 메시지를 표시해준 후에 게임 초기화
    if state == 1:
        canvas.update()
        tkinter.messagebox.showinfo("축하합니다!", "모든 바닥을 칠했습니다!")
        reset()
    # 클리어 불가능 메시지를 표시해준 후에 게임 초기화
    if state == 2:
        tkinter.messagebox.showinfo("망했어요!", "클리어가 불가능합니다\n 다시 시작하세요!")
        reset()
    
    root.after(100, main_proc)

    
root = tkinter.Tk()
root.title("미로를 칠하는 중")
root.bind("<KeyPress>", key_down)
root.bind("<KeyRelease>", key_up)
canvas = tkinter.Canvas(width=800, height=560, bg="white")
canvas.pack()

# 미로 생성
maze = [
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 0, 0, 0, 0, 0, 1, 0, 0, 1],
    [1, 0, 1, 1, 0, 0, 1, 0, 0, 1],
    [1, 0, 0, 1, 0, 0, 0, 0, 0, 1],
    [1, 0, 0, 1, 1, 1, 1, 1, 0, 1],
    [1, 0, 0, 0, 0, 0, 0, 0, 0, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]
for y in range(7):
    for x in range(10):
        if maze[y][x] == 1:
            canvas.create_rectangle(x * 80, y * 80, x * 80 + 79, y * 80 + 79, fill="skyblue", width=0)

img = tkinter.PhotoImage(file=resource_path("mimi_s.png"))
# MYCHR(캐릭터) 태그 추가
canvas.create_image(mx * 80 + 40, my * 80 + 40, image=img, tag="MYCHR")
main_proc()
root.mainloop()

명령어

pyinstaller -w -F --add-data "mimi_s.png;." -i "mimi_s.ico" maze_game.py

 

github: https://github.com/kimjinho1/Python-Game/blob/main/%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9D%B8%20%EA%B2%8C%EC%9E%84%20%EA%B0%9C%EB%B0%9C%20%EA%B8%B0%EC%88%A0/maze_game.ipynb

 

kimjinho1/Python-Game

Contribute to kimjinho1/Python-Game development by creating an account on GitHub.

github.com

728x90
반응형

+ Recent posts