Introduction to Python Applications 2021 - PyGame Sample Codes for Term Project === ###### tags: `Python` `Python and Its Application 2021` ```python= import pygame from random import randint, shuffle pygame.init() window = pygame.display.set_mode((1600, 900)) pygame.display.set_caption("PyGame") # Please use a unicode font. Change 'Arial Unicode MS.ttf' to any proper font file font = pygame.font.Font('Arial Unicode MS.ttf', 16) class Card: # 這是一張撲克牌 def __init__(self, suit, rank, side): self.s = suit self.r = rank self.side = side def blit(self, focus=0): # blit 會回傳一個可以被繪製的物件 # 可以用 window.blit(x.blit(), (x, y)) obj = pygame.Surface((100, 140)) if self.r == 0: obj.fill((13, 139, 58)) return obj elif self.side: obj.fill((127, 127, 127)) else: obj.fill((255, 255, 255)) if focus == 0: pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 3) else: pygame.draw.rect(obj, (255, 0, 0), [0, 0, 100, 140], 9) if self.side: return obj face = '♣♦❤♠'[self.s] + list(' A234567890JQK')[self.r] # Cure 10 if face[-1] == '0': face = face[0] + '1' + face[1] color = (0, 0, 0) if self.s in (0, 3) else (255, 0, 0) obj.blit(font.render(face, True, color), (5, 0)) return obj class Pool: # 代表卡池,也就是維護剩下的卡片 def __init__(self): # 初始化牌組 self.cards = [Card(suit, rank, False) for suit in range(4) for rank in range(1, 14)] shuffle(self.cards) def remaining(self): return len(self.cards) def pop(self): if len(self.cards): return self.cards.pop() else: return None class Pane: # 排堆 def __init__(self, pool): self.pool = pool self.hand = [Card(0, 0, True)] self.focus = False def serve(self): if self.pool.remaining() > 0: self.hand.append(self.pool.pop()) else: self.pool.cards = self.hand[::-1][:-1] self.hand = self.hand[:1] self.setFocus(False) def setFocus(self, focus): # Focus 代表那張手牌正在被點選 self.focus = focus if len(self.hand) > 1 else False def width(self): # 會回傳手牌物件的寬度,來判斷有沒有點擊到手牌 return 100 def blit(self): obj = pygame.Surface((500, 140)) obj.fill((13, 139, 58)) pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 3) txt = font.render("X" + str(self.pool.remaining()), True, (0, 0, 0)) obj.blit(txt, (65, 120)) obj.blit(self.hand[-1].blit(self.focus), (150, 0)) return obj class Slot: # 一個上面的區塊,可以讓你調整牌面 def __init__(self, num, pool): # 初始化的時候抽 5 張牌 self.cards = [pool.pop() for _ in range(num)] for card in self.cards[:-1]: card.side = True self.focus = -1 def height(self): # 會回傳 slot 物件的高度,來判斷有沒有點擊到 slot if len(self.cards) <= 1: return 140 else: return 140 + max(0, len(self.cards) - 1) * 26 def blit(self): obj = pygame.Surface((100, 600)) obj.fill((13, 139, 58)) pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 1) for i, card in enumerate(self.cards): obj.blit(card.blit(), (0, 26 * i)) if self.focus != -1: pygame.draw.rect(obj, (255, 0, 0), [0, self.focus * 26, 100, self.height() - self.focus * 26], 6) return obj def setFocus(self, focus): # Focus 代表那一個 slot 的某部分牌正在被選取 self.focus = focus self.focus = min(self.focus, len(self.cards) - 1) self.focus = max(self.focus, -1) for i in range(self.focus + 1, len(self.cards)): if self.cards[i].r != self.cards[i - 1].r - 1: self.focus = -1 return def merge(self, segment): # 要有牌從手牌或是其他 slot 被移動過來 if len(self.cards) == 0: self.cards = segment self.focus = -1 return 1 elif segment[0].r == self.cards[-1].r - 1: self.cards += segment self.focus = -1 return 1 return 0 class Game(): # 一局遊戲 def __init__(self): self.pool = Pool() self.slots = [Slot(i+1, self.pool) for i in range(7)] self.pane = Pane(self.pool) self.focus = None def setFocus(self, area, card): pa = pc = na = None if self.focus: # 把舊的資料記錄起來並且清除掉 pa, pc = self.focus if pa == "pane": self.pane.setFocus(True) for i in range(7): if pa == "slot" + str(i): self.slots[i].setFocus(-1) pa = i self.focus = [area, card] if area == None: self.focus = None return # 如果是摸手牌 if area == "pane": self.pane.setFocus(True) self.focus[1] = self.pane.focus na = area nc = self.focus[1] # 如果是摸某個 slot for i in range(7): if area == "slot" + str(i): self.slots[i].setFocus(card) self.focus[1] = self.slots[i].focus na = i nc = self.focus[1] if pa == None: return elif type(pa) == int and type(na) == int and pa != na and pc != -1: # 從某個 slot 轉移到另一個 slot segment = self.slots[pa].cards[pc:] if self.slots[na].merge(segment): self.slots[pa].cards = self.slots[pa].cards[:pc] if self.slots[pa].cards: self.slots[pa].cards[-1].side = False elif pa == "pane" and type(na) == int and self.pane.focus: # 從手牌轉移到某個 slot segment = [self.pane.hand[-1]] if self.slots[na].merge(segment): self.pane.hand.pop() self.pane.setFocus(False) def blit(self): obj = pygame.Surface((1600, 900)) obj.fill((13, 139, 58)) obj.blit(self.pane.blit(), (100, 700)) for i in range(len(self.slots)): obj.blit(self.slots[i].blit(), (300 + i * 150, 80)) return obj #### --- 進入遊戲 --- #### game = Game() while True: window.blit(game.blit(), (0, 0)) pygame.display.flip() for e in pygame.event.get(): if e.type == pygame.QUIT: exit(0) if e.type == pygame.MOUSEBUTTONDOWN: x, y = pygame.mouse.get_pos() flg = 1 if 100 <= x <= 200 and 700 <= y <= 840: game.pane.serve() flg = 0 # 你摸到手牌,然後要點擊手牌 if 250 <= x <= 250 + game.pane.width() and 700 <= y <= 840: # pane game.setFocus("pane", True) flg = 0 # 摸某個 slot for i in range(7): if 300 + i * 150 <= x <= 300 + i * 150 + 100 and 80 <= y <= 80 + game.slots[i].height(): game.setFocus("slot" + str(i), (y - 80) // 26) flg = 0 # 如果是無效點擊,就把舊的紀錄清除掉 if flg: game.setFocus(None, None) ``` ### 課堂後版本 ```python= import pygame from random import randint, shuffle pygame.init() window = pygame.display.set_mode((1600, 900)) pygame.display.set_caption("PyGame") # Please use a unicode font. Change 'Arial Unicode MS.ttf' to any proper font file font = pygame.font.Font('Arial Unicode MS.ttf', 16) class Card: # 這是一張撲克牌 def __init__(self, suit, rank, side): self.s = suit self.r = rank self.side = side def blit(self, focus=0): # blit 會回傳一個可以被繪製的物件 # 可以用 window.blit(x.blit(), (x, y)) obj = pygame.Surface((100, 140)) if self.r == 0: obj.fill((13, 139, 58)) return obj elif self.side: obj.fill((127, 127, 127)) else: obj.fill((255, 255, 255)) if focus == 0: pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 3) else: pygame.draw.rect(obj, (255, 0, 0), [0, 0, 100, 140], 9) if self.side: return obj face = '♣♦❤♠'[self.s] + list(' A234567890JQK')[self.r] # Cure 10 if face[-1] == '0': face = face[0] + '1' + face[1] color = (0, 0, 0) if self.s in (0, 3) else (255, 0, 0) obj.blit(font.render(face, True, color), (5, 0)) return obj class Pool: # 代表卡池,也就是維護剩下的卡片 def __init__(self): # 初始化牌組 self.cards = [Card(suit, rank, True) for suit in range(4) for rank in range(1, 14)] shuffle(self.cards) def remaining(self): return len(self.cards) def pop(self): if len(self.cards): return self.cards.pop() else: return None class Pane: # 排堆 def __init__(self, pool): self.pool = pool self.hand = [Card(0, 0, True)] self.focus = False def serve(self): if self.pool.remaining() > 0: self.hand.append(self.pool.pop()) else: self.pool.cards = self.hand[::-1][:-1] for card in self.pool.cards: card.side = True self.hand = self.hand[:1] self.hand[-1].side = False self.setFocus(False) def setFocus(self, focus): # Focus 代表那張手牌正在被點選 self.focus = focus if len(self.hand) > 1 else False def width(self): # 會回傳手牌物件的寬度,來判斷有沒有點擊到手牌 return 100 def blit(self): obj = pygame.Surface((500, 140)) obj.fill((13, 139, 58)) if self.pool.remaining() > 0: obj.blit(self.pool.cards[-1].blit(), (0, 0)) pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 3) txt = font.render("X" + str(self.pool.remaining()), True, (0, 0, 0)) obj.blit(txt, (65, 120)) obj.blit(self.hand[-1].blit(self.focus), (150, 0)) return obj class Slot: # 一個上面的區塊,可以讓你調整牌面 def __init__(self, num, pool): # 初始化的時候抽 num 張牌 self.cards = [pool.pop() for _ in range(num)] self.cards[-1].side = False self.focus = -1 def height(self): # 會回傳 slot 物件的高度,來判斷有沒有點擊到 slot if len(self.cards) <= 1: return 140 else: return 140 + max(0, len(self.cards) - 1) * 26 def blit(self): obj = pygame.Surface((100, 600)) obj.fill((13, 139, 58)) pygame.draw.rect(obj, (0, 0, 0), [0, 0, 100, 140], 1) for i, card in enumerate(self.cards): obj.blit(card.blit(), (0, 26 * i)) if self.focus != -1: pygame.draw.rect(obj, (255, 0, 0), [0, self.focus * 26, 100, self.height() - self.focus * 26], 6) return obj def setFocus(self, focus): # Focus 代表那一個 slot 的某部分牌正在被選取 self.focus = focus self.focus = min(self.focus, len(self.cards) - 1) self.focus = max(self.focus, -1) if self.focus >= 0 and self.cards[self.focus].side: self.focus = -1 return for i in range(self.focus + 1, len(self.cards)): if self.cards[i].r != self.cards[i - 1].r - 1: self.focus = -1 return def merge(self, segment): # 要有牌從手牌或是其他 slot 被移動過來 if len(self.cards) == 0 and segment[0].r != 13: return 0 if len(self.cards) > 0 and segment[0].r != self.cards[-1].r - 1: return 0 self.cards += segment self.focus = -1 return 1 class Game(): # 一局遊戲 def __init__(self): self.pool = Pool() self.slots = [Slot(i+1, self.pool) for i in range(7)] self.pane = Pane(self.pool) self.focus = None def setFocus(self, area, card): pa = pc = na = None # previous area, previous card, now area if self.focus: # 把舊的資料記錄起來並且清除掉 pa, pc = self.focus if pa == "pane": self.pane.setFocus(True) for i in range(7): if pa == "slot" + str(i): self.slots[i].setFocus(-1) pa = i self.focus = [area, card] '''if area == None: self.focus = None return''' # 如果是摸手牌 if area == "pane": self.pane.setFocus(True) self.focus[1] = self.pane.focus na = area # nc = self.focus[1] # 如果是摸某個 slot for i in range(7): if area == "slot" + str(i): self.slots[i].setFocus(card) self.focus[1] = self.slots[i].focus na = i # nc = self.focus[1] if pa == None: return elif type(pa) == int and type(na) == int and pa != na and pc != -1: # 從某個 slot 轉移到另一個 slot segment = self.slots[pa].cards[pc:] if self.slots[na].merge(segment): self.slots[pa].cards = self.slots[pa].cards[:pc] if self.slots[pa].cards: self.slots[pa].cards[-1].side = False elif pa == "pane" and type(na) == int and self.pane.focus: # 從手牌轉移到某個 slot segment = [self.pane.hand[-1]] if self.slots[na].merge(segment): self.pane.hand.pop() self.pane.setFocus(False) def blit(self): obj = pygame.Surface((1600, 900)) obj.fill((13, 139, 58)) obj.blit(self.pane.blit(), (100, 700)) for i in range(len(self.slots)): obj.blit(self.slots[i].blit(), (300 + i * 150, 80)) return obj #### --- 進入遊戲 --- #### game = Game() while True: window.blit(game.blit(), (0, 0)) pygame.display.flip() for e in pygame.event.get(): if e.type == pygame.QUIT: exit(0) if e.type == pygame.MOUSEBUTTONDOWN: x, y = pygame.mouse.get_pos() flg = False # 你摸到排堆 if 100 <= x <= 200 and 700 <= y <= 840: game.pane.serve() flg = True # 你摸到手牌,然後要點擊手牌 if 250 <= x <= 250 + game.pane.width() and 700 <= y <= 840: # pane game.setFocus("pane", True) flg = True # 摸某個 slot for i in range(7): if 300 + i * 150 <= x <= 300 + i * 150 + 100 and 80 <= y <= 80 + game.slots[i].height(): game.setFocus("slot" + str(i), (y - 80) // 26) flg = True # 如果是無效點擊,就把舊的紀錄清除掉 if not flg: game.pane.setFocus(False) game.focus = None # game.setFocus(None, None) ```