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)
```